This is what dependency injection solutions are for. Yes, this is another technology to add to your project, but as soon as you start using DI, you never look back.
The real problem is that you are trying to make this decision in your view models when you really need to use control inversion and make the decision above. The WPF / MVVM application will want a context for each form so that the changes are presented only after the user has finished editing, as well as to enable the user to discard the changes. I know that you are not using this in a web application, but a well-designed architecture means you should be able to, in which case you will need context for each request. You might want to write a console program that populates the database with static data, in which case you might need a global / singleton context for performance and ease of use. Finally, your unit tests should also mock the context, perhaps based on each test. All four of these cases should be configured in your injection infrastructure, and your viewing models should neither know nor care about them.
Here is an example. I personally use Ninject, which is specifically designed for .NET. I also prefer NHibernate, although the choice of ORM does not matter here. I have session objects that have different scope requirements, and this is set in the Ninject module that initializes my ORM classes:
var sessionBinding = Bind<ISession>().ToMethod(ctx => { var session = ctx.Kernel.Get<INHibernateSessionFactoryBuilder>() .GetSessionFactory() .OpenSession(); return session; }); if (this.SingleSession) sessionBinding.InSingletonScope(); else if (this.WebSession) sessionBinding.InRequestScope(); else sessionBinding.InScope(ScreenScope);
This sets the scope of ISession, which is the NHibernate equivalent of your context class. My repository classes that manage in-memory database objects contain a link to the session with which they are associated:
public class RepositoryManager : IRepositoryManager { [Inject] public ISession Session { get; set; } ... etc... {
The [Inject] attribute tells Ninject to automatically fill in this field using the scope rules that I configured. So far, all this is happening in my domain classes, but it extends to my model layer. In my scope rules, I pass an object called "ScreenScope", and although I will not go into it here, it basically means that at any time I request a session object in my ScreenViewModel or any view models that it has as members (including their own children), the same ISession object is automatically created and passed to all of them. Using the scope of the DI, I donβt even need to think about it, I just declare the members with the [Inject] attribute, and this happens:
public class ScreenViewModel { [Inject] public CustomerService CustomerService { get; set; } [Inject] public SalesService SalesService { get; set; } [Inject] public BillService BillService { get; set; } ...etc... }
These service classes contain the RepositoryManager set that was introduced, and since they are all in ScreenViewModel, the ISession object will be the same, at least in my WPF assembly. if I switch to my MVC assembly, they will be the same for all the view models created for this request, and if I switch to the console assembly, it uses the same ISession for everything in the whole program.
TL; DR: Use dependency injection and the scope of your contexts in one form.