locked
EFCore, DatabaseFirst. How to move generic repository interface to another assembly? RRS feed

  • Question

  • User1725160972 posted

    I use this tutorial to create a Database model.

    I decided to create another assembly for abstractions such as IRepository, IFooService by reading this quote of "Programming .NET Components" by Juwal Lowy:

    Because interfaces can be implemented by multiple components, it's good practice to put them in a separate assembly from that of the implementing components.

    My solution structure could look like this:

    PersonsApp.Solution
    --PersonsApp.WebUI
       -- Controllers (PersonController)
    --PersonApp.Common
       --Core folder
          -IGenericRepository.cs (Abstraction)
          -IUnitOfWork.cs (Abstraction)  
       --Repositories folder
          -IPersonRepository.cs (Abstraction)
    --PersonApp.Persistence  
      --Infrastructure folder
          -DbDactory.cs (Implementation)
          -Disposable.cs (Implementation)
          -IDbFactory.cs (Abstraction)
          -RepositoryBase.cs (Abstraction)
      --Models folder(Here we have DbContext, EF models (Implementation))      
          -Person.cs (Implementation)
          -PersonContext.cs (Implementation)
      --Repositories folder
          -PersonRepository.cs (Implementation)

    However, PersonApp.Persistence project has reference to PersonApp.Common. But my project PersonApp.Common has also need a reference to PersonApp.Persistence because I would like to create a IPersonRepository.cs in Person.Common:

    public interface IPersonRepository : IRepository<Person> {}

    The following error is thrown, when I try to add reference PersonApp.Persistence into PersonApp.Common:

    A reference to 'PersonApp.Persistence' could not be added. Adding this project as a reference would cause a circular dependency.

    However, I really would like to have separate assembly for interfaces and another assembly for EntityFramework.

    How can I move repository interface to another assembly? In addition, I would like to be able in future to keep the database schema in sync with the EF Core model by preserving data.

    I use DatabaseFirst. Thank in advance. Any help would be greatly appreciated!

    Sunday, September 8, 2019 11:58 AM

Answers

  • User475983607 posted

    I usually place interface in the same file that contains the implementation.  An interface is just a type that has definitions and no code.  An interface allows you to swap code (implementation) as long as the implementations have the same signatures.  Usually, the interfaces will be in the same project as your implementation.  That's not a rule but easier to find the implementation.

    Since you are using Core, your the DbContext should be injected into the Repo/UoW.  It is important to understand that the Repo/UoW sits between your business layer and your data persistence.  Your business layer never talks directly to the DbContext, everything goes through the Repo/UoW.  There is simply no logical reason for a circular reference to exist because the DbContext is a member of the Repo/UoW - encapsulation.  Think of your selected design approach like this, you must write a copy of every possible DbContext implementation in your Repo/UoW. 

    I'm guessing this is your first Repo/UoW pattern.  Usually what happens is once you get to writing business logic you find that CRUD operations requiring a join, which makes up the majority of any project, are very difficult to write and are resource intensive.  You can't add this logic to the Generic Repo as that will ripple through every generic repo.  So you'll end up writing services where you'll spend most of your dev efforts.

    Good luck

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 10, 2019 10:53 AM

All replies

  • User475983607 posted

    The Unit of work and generic repositories should reference EF through an Interface.  Your EF library should never reference unit of work and generic repositories and upper layers should never access EF directly.   The main reason to implement a Unit of Work and generic repository is so the data access layer can be swapped out without affecting the rest of the application.  This is a very uncommon feature and one that rarely happens in real life

    IMHO, you should consider dropping the unit of work and generic repositories.  These are anti pattern when used with EF and the patterns will restrict your business layer resulting in redundant and hard to maintain code.  Most likely, part of your current issue is related these restrictions as you want the flexibility of EF in upper application layers but everything must go through Unit of Work.

    Sunday, September 8, 2019 2:49 PM
  • User1725160972 posted

    The Unit of work and generic repositories should reference EF through an Interface.  Your EF library should never reference unit of work and generic repositories and upper layers should never access EF directly.   

    Please, tell me how can I do it. I am really trying to do it. However, in my view it is not possible as we use:

    1. Database First approach
    2.  If I move model classes of Enity Framework into another assembly, then I will not able to update my model classes, cannot add User Defined Functions, Stored Procedures.

    Please, tell me how can I do it if it is possible to decouple model from Entity Framework assembly.

    Sunday, September 8, 2019 7:25 PM
  • User475983607 posted

    NiceStepUp

    Please, tell me how can I do it. I am really trying to do it. However, in my view it is not possible as we use:

    1. Database First approach

    Database first does not exist in EF Core.

    NiceStepUp

    1.  If I move model classes of Enity Framework into another assembly, then I will not able to update my model classes, cannot add User Defined Functions, Stored Procedures.

    Please, tell me how can I do it if it is possible to decouple model from Entity Framework assembly.

    There's nothing stopping you from placing EF Entities in class library.  However, I think you miss the point of your selected design patterns.  You do not want EF entities to leak out beyond the unit of work and generic repositories.  All data access should go through the unit of work and generic repositories.

    IMHO, DDD is a far better approach than Unit of Work and Generic Repositories as EF Core is already a Unit of Work and Repository.  Anyway, the standard EF docs explain how to design using these patterns.

    https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-implemenation-entity-framework-core

    Monday, September 9, 2019 1:54 PM
  • User1725160972 posted

    However, I think you miss the point of your selected design patterns.  You do not want EF entities to leak out beyond the unit of work and generic repositories.  All data access should go through the unit of work and generic repositories.

    Do you mean that IPersonRepository.cs  interface should be placed inside PersonApp.Persistence? Do I understand correctly?

    --PersonApp.Persistence  
      --Infrastructure folder
          -DbDactory.cs (Implementation)
          -Disposable.cs (Implementation)
          -IDbFactory.cs (Abstraction)
          -RepositoryBase.cs (Abstraction)
    --Repository
          - IPersonRepository
    Tuesday, September 10, 2019 7:35 AM
  • User475983607 posted

    I usually place interface in the same file that contains the implementation.  An interface is just a type that has definitions and no code.  An interface allows you to swap code (implementation) as long as the implementations have the same signatures.  Usually, the interfaces will be in the same project as your implementation.  That's not a rule but easier to find the implementation.

    Since you are using Core, your the DbContext should be injected into the Repo/UoW.  It is important to understand that the Repo/UoW sits between your business layer and your data persistence.  Your business layer never talks directly to the DbContext, everything goes through the Repo/UoW.  There is simply no logical reason for a circular reference to exist because the DbContext is a member of the Repo/UoW - encapsulation.  Think of your selected design approach like this, you must write a copy of every possible DbContext implementation in your Repo/UoW. 

    I'm guessing this is your first Repo/UoW pattern.  Usually what happens is once you get to writing business logic you find that CRUD operations requiring a join, which makes up the majority of any project, are very difficult to write and are resource intensive.  You can't add this logic to the Generic Repo as that will ripple through every generic repo.  So you'll end up writing services where you'll spend most of your dev efforts.

    Good luck

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 10, 2019 10:53 AM
  • User1725160972 posted

    mgebhard

    Usually what happens is once you get to writing business logic you find that CRUD operations requiring a join, which makes up the majority of any project, are very difficult to write and are resource intensive.  You can't add this logic to the Generic Repo as that will ripple through every generic repo.  So you'll end up writing services where you'll spend most of your dev efforts.

    Thank you very much for the answers!

    However, I can override methods of base repository and write all joins into overridden methods. is it okay to write code into overridden methods? Do you see any problems in this approach(writing code in overridden methods)?

    Tuesday, September 10, 2019 8:03 PM