1

I have been trying to create a Repository Pattern along with Dependency injection, But Looks like I am missing some simple step. Here is my code

public class HomeController 
{
   private readonly ILoggingRepository _loggingRepository;
   public HomeController(ILoggingRepository loggingRepository)
   {
     _loggingRepository = loggingRepository;
   }

   public void MyMethod()
   {
        string message = "MyMessage Called";
       _loggingRepository .LogMessage(message);
    }
}

// ILoggingRepository.cs
public interface ILoggingRepository
{
   void LogMessage(string message);
}

// LoggingRepository.cs
public class LoggingRepository : ILoggingRepository
{ 
     public void LogMessage(string message)
     {
        using (var dbContext = new DbContext())
        {
            var serviceLog = new Log() { Message = message, Logged = DateTime.UtcNow };
            dbContext.Logs.Add(serviceLog);
            dbContext.SaveChanges();
        }
   }
}

This works perfectly all right so far, but the problem arises when i make more than one repository calls.

Now I know that Entity framework 6.0 has inbuilt unit of work representation so I didn't created a UnitofWork Interface or class But the problem appears when I do something like this in two different transactions. Lets say

Area area = _areaRepository.GetArea(); // Line 1
area.Name = "NewArea"; // Line 2
_areaRepository.SaveArea(area);  // Line 3   

now because it _areaRepository creates a new DbContext in Line 3, it doesn't changes the name of area as it doesn't consider EntityState.Modified I have to explicitly set that, which isn't correct.

So I guess I need to do all this in single Transaction, Where I am doing wrong here ? What is the correct and best way to achieve this, Should I inject my DbContext also into the repository?

Pankaj
  • 2,618
  • 3
  • 25
  • 47
  • http://stackoverflow.com/questions/14857813/entity-framework-savechanges-not-updating-the-database I don't think transaction is the case. Maybe you need to call .UpdateObject? – Maxim Kosov May 15 '17 at 14:14
  • I made a question about all this: http://stackoverflow.com/questions/41769475/solid-principles-repository-pattern-and-entityframework-cache-in-asp-net-mvc. The answer was: Entity Framework already uses Repository Pattern and unit of work. Dont implement your self. But your problem is that you are not using the same db context between all your repositories. Inject the db context in the repositories constructors. You can have a repository per request, or per thread, depending what kind of project will use your model library (web, desktop, windows service, etc) – aperezfals May 15 '17 at 14:33
  • @AlejandroPérezFals, My project is a Windows service, (Forget about the controller class written above, that was just an example to show how I am injecting dependency), so I need a repository per thread. – Pankaj May 15 '17 at 14:46

3 Answers3

0

This is how I doit all times: If dont use Repository or Unit of Work layers, because Entity Framework db Context already implements those patterns. So, I only have a Service layer:

public interface IBaseService<VO, ENT>{
    IQueryable<VO> GetAll();

    VO Get(object id);
}

public abstract class BaseService<VO, ENT> : IBaseService<VO, ENT>{

    MyContext db;

    public BaseService(MyContext db){
        this.db = db;
    }

    public IQueryable<VO> GetAll(){
        return db.Set<ENT>().ProjectTo<VO>();
    }
}

A service class have a dbContext injected in the constructor. This classes are located in a Service library. Then, how the dbContext and the service are resolved is a problem of the project who will be using them. The ProjectTo method is an extension for IQueryable from the Automapper Nuget. For example:

A Windows Service needs all services instance in the same thread shares the same dbContext. So, in the windows service project, I use Ninject https://www.nuget.org/packages/Ninject/4.0.0-beta-0134, this library is a dependency resolver, wich I use to configure how dependencies are builded, creating a Kernel, like this:

var kernel = new StandardKernel();

kernel.Bind<MyContext>().ToSelf().InThreadScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InThreadScope();

I you are creating a Web project, you will need to install a aditional nuget (Ninject.WebCommon, Ninject.Web.COmmon.WebHost, Ninject.MVC5) to provide a .InRequestScope() method to the binding configuration, like this:

var kernel = new StandardKernel();

kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InRequestScope();

You need setup those kernel when the app startup. In a web project is in the global.asax, in a windows service project, should be in the Service constructor:

You can visit www.ninject.org/learn.html to learn more about ninject. But, there are othres like Autofac or Caste Windsor, it is up to you. If you like to keep using the repository pattern, just use Ninject inject them into the Service layer, like i did with the dbContext.

aperezfals
  • 1,341
  • 1
  • 10
  • 26
-1

The best approach is to have one instance of DbContext, injecting it on each repository implementation. That way you will have a single instance of the database context, so EF will be able to detect changes on the entity objects.

If you need to use isolated dbContexts as in your example, then you need to explicitly set the state of the object as Modified.

Depending on the type of project, you should set the context on a specific scope. For example, for web applications one option is to use instance per Web request (per lifetime scope). Check this url where you can see a good explanation of the different instance scopes.

The using statement simply creates a new scope, executing the Dispose() method after the code block. EF does a lot on the background to maintain the UoW and state of the objects, but in your case, with the using, you are not using this fature.

jparaya
  • 1,331
  • 11
  • 15
  • I don't think one instance of DbContext (singleton) is a good approach. – Pankaj May 15 '17 at 12:13
  • It is not a singleton, but a common dbcontext per request. Singleton should be if it is per application domain, which is not the case – jparaya May 15 '17 at 20:21
-2

First, a DbContext is a repository. If you want to wrap it in a custom repository, they should have the same lifecycle.

Second, your Unit-of-work is your controller. The repository should be scoped to unit-of-work.

This means that your repository needs to be Disposable, since the DbContext is.

So something like:

    public interface ILoggingRepository : IDisposable
    {
        void LogMessage(string message);
    }

    // LoggingRepository.cs
    public class LoggingRepository : ILoggingRepository
    {
        MyDbContext db;
        public LoggingRepository(MyDbContext db)
        {
            this.db = db;
        }

        public void Dispose()
        {
            db.Dispose();
        }

        public void LogMessage(string message)
        {

            var serviceLog = new MonitoringServiceLog() { Message = message, Logged = DateTime.UtcNow };
            db.MonitoringServiceLogs.Add(serviceLog);
            db.SaveChanges();

        }
    }

If your ILoggingRepository wan't a database, it might be a file or something else that is expensive to create or open and needs to be closed.

David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • 1
    In general, `IDisposable` shouldn't be added to interfaces. Disposable resource used in one specific _implementation_ of `ILoggingRepository` only. Otherwise it's a leaky abstraction. Also, `LoggingRepository` shouldn't dispose `MyDbContext` since context is injected and could be used somewhere else. – Maxim Kosov May 15 '17 at 14:03
  • Nope. An service interface like this should be IDisposable if any of its implementations must be IDisposable. Typically the client code gets instances from a Factory and doesn't know the runtime type, so the only way to ensure that DbLoggingRepository gets propery Disposed is to force all ILoggingRepository types to support Dispose(). – David Browne - Microsoft May 15 '17 at 16:08
  • 1
    This is thats called _leaky abstraction_. What if first implementation wasn't `IDisposable` and you already use it all the way. Then, you create second implementation which has `DbContext` injected in it's constructor, so you make whole interface `IDisposable`. Now you must change every usage of your interface, add `using`, probably create a factory. – Maxim Kosov May 15 '17 at 19:41
  • Right, which is exactly why you need to consider whether your interface _supports_ implementations that require lifecycle management. IDisposable is how you declare that implementations may require cleanup. That's not a "leaky abstraction" it's just contract design. The contract must require Dispose() for implementations to rely on it. – David Browne - Microsoft May 16 '17 at 02:00