The thing is this, LINQ to SQL is not a true Object Relation Mapper (ORM), it is a data access layer generator. You can make it be an ORM by going deep by hand editing XML files and playing with SqlMetal and whatnot, but where it shines is as a DAL.
The idea behind an ORM is this. You have your SQL database, and your domain objects. To design a database properly, you are going to do things (like normalization) that logically don't translate into a properly designed object model, and vice-versa. This is called "Impedance Mismatch", the role of an ORM is to deal with that mismatch in a clean, effective, and efficient way. The less painful database interaction is almost a secondary thing.
The idea behind a repository is that it encapsulates all persistence logic and dependencies on infrastructure from the rest of your application. When your application needs a Customer object, it shouldn't have to know whether it is coming from SQL Server, MySQL, an XML file, or ASP.NET Membership. Once you have that de-coupling, any changes you make to your persistence story have no effect on the rest of your application.
With that in mind, it gets more clear why he did what he did. LINQ to SQL is used to generate the DAL, but the only thing that should be known about the DAL is the repository, so a translation is made to his domain objects. That way he can refactor his domain model without worrying about his persistence story, and he can refactor his database without worrying about rippling effects through his application. He could also start coding on business logic before deciding on questions like what ORM to use, or even where to store his data.
If he were to use a real ORM (like NHibernate), that mapping code gets handled elsewhere (either in XML or bootstrapping classes). I think LINQ to SQL (and Robs open source DAL, SubSonic) are great projects, but more designed for smaller, two-tier applications where something like the repository pattern is overkill. The storefront is also a good illustration of why the additional complexity of NHibernate can be important. He could have saved himself a lot of code by going with something built to handle that sort of scenario, rather than doing it all manually.
What makes you say that either a singleton or a static method isn't thread-safe? Usually both should be implemented to be thread-safe.
The big difference between a singleton and a bunch of static methods is that singletons can implement interfaces (or derive from useful base classes, although that's less common, in my experience), so you can pass around the singleton as if it were "just another" implementation.
Best Answer
If you want to create a pluggable architecture to be able to swap out the repository layer on the fly then you will need to create everything behind an interface and then use something like StructureMap to dynamically swap in what you need when you need it.
You would want to define a repository class like AccountRepository. One for linq to sql LSAccountRepository and EFAccountRepository. Both of these would inherit from IAccountRepository and have methods such as GetAccountByID, SaveAccount, DeleteAccount, etc.
Then using StructureMap you would have syntax like so to load the appropriate repository based on the system that you are loading in.
IAccountRepository _accountRepository = ObjectFactory.GetInstance();
Then via your configuration you can specify the default implementation of IAccountRepository and swap this out to point to either implementation at any time.
If this is too complex then a dependancy injection patten can be used in that you can have a method with a parameter of IAccountRepository. This method would be called by a controller (MVP or MVC) and you can pass in the appropriate reference at that time. This way you do not directly instantiate the repository inside of the method that might need one repository vs. another.
Even if you do decide to do a DI pattern you can still use StructureMap. When you instantiate an object that has other dependancies in it that StructureMap knows about (in the constructor) then StructureMap will also load up those objects as well. Then the caller of the object that has the method that you need will be the only loose coupling that is needed as StructureMap will dynamically take care of the dirty work for you.