In EF, DbContext and DbSet, generally speaking, implement UnitOfWork and Repository out of the box, respectively. On the Internet, there are thousands of examples of how people, following clearly in alphabetical order, wrap their hands in their classes, which implement their interfaces. Something like this:

public interface IUnitOfWork : IDisposable { IRepository<T> GetRepository<T>() where T : class; void SaveAllChanges(); } public interface IRepository<T> : IDisposable where T : class { IQueryable<T> Entities(); void Update(T entity); void Add(T entity); void Remove(T entity); bool Contains(T entity); } 

It turns out a kind of abstraction over abstraction, in many places they write that this should not be done, but nowhere is an example of how to implement it correctly. I have a three-layer project, initially did the same through repo and uow, later removed the repositories and uow, but when I add objects from different classes to each other I catch: System.InvalidOperationException: "It was not possible to determine the connection between the two objects, as they are tied to different ObjectContext objects. " As I understand it, this is due to the fact that every time I declare a new instance of the data context, this is what I have right now in the BLL:

  public class EntityService : IEntityService { private MyContext db; public EntityService(string connectionString) { db = new MyContext(connectionString); } ... } 

How can I correctly transfer one instance of the data context to these services without explicitly re-implementing UoW?

  • The connection between the two objects could not be determined because they are tied to different ObjectContext objects. - maybe you just need to Detach object from one context, then Attach to another context? - Alexander Petrov
  • @Alexander Petrov, in my opinion, would such a decision be too crutch or not? - Devoli

2 answers 2

You can pass to the DbContext constructor which will return some DI container. In the container itself, you have already configured the life cycle of the object

 private DbContext db; public EntityService(DbContext context) { db = context; } 

In containers we register MyContext for DbContext . If you are sure that the type of your registered context will not change, then you can also make

 private MyContext db; public EntityService(DbContext context) { db = (MyContext)context; } 
  • "In containers, register MyContext for DbContext." I understand this dependence can be done through Ninject, but he doesn’t want to do something. - Devoli
  • Yes, you can through Ninject. Does MyContext inherit DbContext? - Vadim Prokopchuk
  • Yes, inherits, but does not want to register a dependency - Devoli
  • @Devoli and how did you try? So 'kernel.Bind <DbContext> (). To <MyContext> (). '? - Vadim Prokopchuk
  • Upd. It seems to have made an addiction, did the same as yours, and what should we have in the DI-container? - Devoli

If you skip the intermediate stages, then there are two options (and a half):

Option 1

If you do not write tests, then you do not need an abstraction IRepository in this form. Create a context at the very top, in a method that corresponds to one business transaction with you, associate objects with each other, then call SaveChanges once - and EF will figure it out.

This is an ideology embedded in EF, and attempts to go against it cause a lot of code and pain.

Option 1.01

The variant that has been customary since the time of NHibernate is to make the context for the request (from the user), put it into the HttpContext (directly or through IoC) and more or less keep up to the point where you have to perform two separate operations in one request. Or write the operation error in the log. When the moment comes, rewrite to [ThreadStatic] / <AsyncLocal> and live on.

Option 2

If you need both UoW, and tests, and a repository (for the sake of tests and for the sake of localizing queries), you will have to wind up something like:

IoW interface as a repository access point:

 public interface IUnitOfWork : IDisposable { IEntity1Repository Entity1Repository { get; } IEntity2Repository Entity2Repository { get; } void Save(); } 

Its implementation in the form

 public class UnitOfWork : IUnitOfWork { static AsyncLocal<UnitOfWork> _root = new AsyncLocal<UnitOfWork>(); private readonly SomeModel _context; public UnitOfWork() { if (_root.Value == null) { this._context = new SomeModel(); _root.Value = this; } else { this._context = _root.Value._context; } } public void Save() { if (_root.Value == this) { _context.SaveChanges(); } } public IEntity1Repository Entity1Repository => new Entity1Repository(_context); public void Dispose() { if (_root.Value == this) { _context.Dispose(); _root.Value = null; } } } 

Because If you want the repository to be wet and context, you will have to add an interface to create a UoW:

 public interface IUnitOfWorkFactory { IUnitOfWork Create(); } 

insert it as dependencies in services and use something like this:

 public class SomeService : ISomeService { [Dependency] public IUnitOfWorkFactory UoWFactory { get; set; } public List<Entity1> GetAllEntitiesList() { using (var uow = UoWFactory.Create()) { // добавить вызов Save в тех методах, которые действительно что-то меняют return uow.Entity1Repository.GetAll(); } } } 

and mock like this:

 var list = new List<Entity1> { new Entity1() }; var repo = Mock.Of<IEntity1Repository>(repo => repo.GetAll() == list); var uow = Mock.Of<IUnitOfWork>(u => u.Entity1Repository == repo); var uowFactory = Mock.Of<IUnitOfWorkFactory>(f => f.Create() == uow); var service = new SomeService() { UoWFactory = uowFactory }; var result = service.GetAllEntitiesList(); CollectionAssert.AreEqual(list, result); 

Related link, with a bunch of other insertion options: Survey of Entity Framework Unit of Work Patterns

  • Is it possible to learn more about 1 method? Not quite understood its implementation. And what about the 2 ways, initially had a similar implementation, only instead of the factories used Ninject. - Devoli
  • The first way to @Devoli is to simply use context as is. Without repositories and other wrappers. - PashaPash