When to use Entity Framework with Repository Pattern?

Asked

Viewed 8,159 times

61

Lately I have several examples of Repository Pattern homemade on questions about Entity Framework.

But the Entity Framework already implements a Repository Pattern, which in this case is the Unit Of Work.

Many of these repositories act as a false repository that encapsulates the truth repository (Dbcontent) to perform basic operations such as Getall, Update, Insert, etc.

In this way, I cannot see benefits in this type of implementation, and can even bring greater complexity to the project itself.

Seria Repository Pattern a new Hungarian Notation, where many programmers still put abbreviations of Type as prefix of the variable in languages with OO, when this type of notation should mention the nature of the variable as prefix and is unnecessary in OO languages?

Maybe I’m not getting it out of the box, so I’d like examples with Repository Pattern adding value.

  • This answer speaks of situations where repositories add value: http://answall.com/a/51439

  • What is Hungarian Notation? kk

  • @Rod, do you know the habit of putting an object type abbreviation in the variable name? type var strNome = "Toby Mosque", this is what some understand by Notation Hungara... whereas the current intention was to use an abbreviation of the data type in the variable, as for example a certain datum s or unsafe u, as in the example: var sValue = 515; //endereço seguro em memoria and var uValue = 6874168416; //endereço não seguro... In any case this habit is unnecessary for modern languages, being useful only for lower level languages.

  • @Tobymosque understood, wow, never crossed my mind to use that kkkk nor from the time of the vb6 did not use that hauhauha

  • @Rod, but surely you’ve seen someone using it, probably without understanding why.

  • @Tobymosque truth...vi até em Procedure sql hauhauha

  • sp_as a prefix to stored procedure in itself is already polemic.

Show 2 more comments

3 answers

68


Never

Simple as that. Implementing one repository on top of another doesn’t make any sense. This is easy to see by analyzing how the DbSet.

DbSet implements IEnumerable and IQueryable (because IQueryable implements IEnumerable). Calling extension methods that return IQueryable, return is an object that accumulates filters. For example:

var query = db.Entidades.Where(x => x.EntidadeId > 25);

Here I’m not executing anything. I’m just getting an object that, in its resolution for an enumeration, will return me objects whose value of EntidadeId is greater than 25. I can accumulate predicates without executing the code. Suppose the object query from the previous example, I can do the following:

query = query.OrderBy(x => x.Nome);

In the implementations I see around, all methods solve the return to IEnumerable, which forces the database to execute the enumeration resolution (hence SQL), accumulating superfluous results in memory and increasing the search execution time because the result is unnecessarily higher.

Below I explain the great fallacies of who defends repository on top of Entity Framework, as well as give examples of the above to make the explanation clearer.

Fallacy 1: "It is more practical to use."

I don’t see any practicality in it:

    public IEnumerable<Model.Article> GetArticles()
    {
        return _context.Articles;
    }

The example speaks for itself. Articles is a DbContext, implementing IQueryable (implementing IEnumerable). On returning IEnumerable, we are forcing the conversion of IQueryable in IEnumerable, i.e., executing the entire query (as a TABLE SCAN, in fact, because there are no filter predicates defined).

Besides being a useless encapsulation, forces the bank to work wrong, makes the application work wrong and accumulates memory for operations that do not need.

Fallacy 2: "The idea is not to repeat highly complex logics several times, in favor of DRY"

If the complex logic is repeated several times, the solution is then to standardize 100% of the system (which is possibly 20% or less which is really complex) or only the 20% complex?

How about Extensions to build the research?

public static class MinhaPesquisaComplexaExtensions 
{
    public static IQueryable<MinhaEntidade> MinhaCondicaoComplexa(this IQueryable<MinhaEntidade> conjunto)
    {
        return conjunto.Where(/* Coloque aqui a condição complexa */);
    }
}

Use:

var retornoComplexo = context.Entidades.MinhaCondicaoComplexa().Where(/* Sim, posso filtrar mais */).ToList();

Remembering I don’t need to wear .ToList() until such time as I need to resolve the enumeration. That is, the query can be executed only at the last moment, taking full advantage of the lazy behavior implemented in the framework.

Fallacy 3: "It is easier to change the ORM."

This one I really like. The easiest is to refactor context statements, for example, this one from an application that uses an Identity context:

private ApplicationDbContext db = new ApplicationDbContext();

For:

private IApplicationDbContext db = new ApplicationDbContext();

IApplicationDbContext would be to extract the interface from its context (which is a repository) so yes we can implement a repository on top of this new interface, ie:

public class MeuContextoDapper: IApplicationDbContext
{ ... }

Then you use the repository you want.

Obviously, the DbSets need to be rewritten. I do not recommend using only IDbSet because some extension methods only exist in DbSet. Better extract the interface from DbSet ensuring the methods of extension.

Fallacy 4: "EF encapsulates repository abstraction and infrastructure, so it takes an extra layer to separate business logic from infrastructure."

Wrong. Note that the technological implementation is separate from the abstraction of the repository since version 6.

Furthermore, if this were the case, it would not be possible, for example, to change the Connection Factory. See some examples:

SQL Server

  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>

SQL Server Localdb

  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="mssqllocaldb" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>

Mysql

  <entityFramework>
    <defaultConnectionFactory type="MySql.Data.Entity.MySqlConnectionFactory, MySql.Data.Entity.EF6"></defaultConnectionFactory>
    <providers>
      <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6"></provider>
    </providers>
  </entityFramework>

Oracle

  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="Oracle.ManagedDataAccess.Client" type="Oracle.ManagedDataAccess.EntityFramework.EFOracleProviderServices, Oracle.ManagedDataAccess.EntityFramework, Version=6.121.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />
    </providers>
  </entityFramework>

Firebird

<entityFramework>
    <defaultConnectionFactory type="FirebirdSql.Data.EntityFramework6.FbConnectionFactory, EntityFramework.Firebird" />
    <providers>
        <provider invariantName="FirebirdSql.Data.FirebirdClient" type="FirebirdSql.Data.EntityFramework6.FbProviderServices, EntityFramework.Firebird" />
    </providers>
</entityFramework>

Postgresql

<entityFramework>
  <defaultConnectionFactory type="Npgsql.NpgsqlFactory, Npgsql" />
  <providers>
    <provider invariantName="Npgsql" type="Npgsql.NpgsqlServices, Npgsql.EntityFramework" />
  </providers>
<entityFramework>

Fallacy 5: "Not using repositories with Entity Framework causes high coupling and hurts the principles of SOLID and good OO programming".

Let’s review the SOLID:

  • S - Single Responsibility Principle, Principle of Single Liability: A class should have a single responsibility, not a Swiss army knife with thousands of functions;
  • The - Open Closed Principle, Open/Closed Principle: In inheritance, derived classes should avoid having peculiar methods and elements when public. Better alternatives are polyformism, the use of interfaces (contracts) and extensions (Extensions, when there is);
  • L - Liskov Substitution Principle, Principle of Liskov’s Substitution: If we have two classes, father and daughter, then the parent class objects in a program can be replaced by the daughter class objects without having to change the properties of this program;
  • I - Interface Segregation Principle, Principle of Interface Segregation: An interface should not be fat, that is, the objects that implement it should not need to implement a lot of methods that will mostly not be used;
  • D - Dependency Inversion Principle, Principle of Inversion of Dependence: Abstractions should not depend on details. Details should depend on abstractions, i.e., if a class or functionality is used in many places with diverse class possibilities, it should be replaced by a generic term or an interface.

In short, this statement does not make much sense.

Here it should be noted that the implementation of a repository on top of the Entity Framework hurts, first, the Principle of Interface Segregation, because CRUD operations are implemented by methods that already exist:

  • Select everything: ToList(), AsEnumerable();
  • Select a: First(), FirstOrDefault(), Single(), SingleOrDefault();
  • Any other selection: Where() followed by ToList() or AsEnumerable;
  • Insert: Add();
  • Edit: Entry(objeto).State = EntityState.Modified;
  • Rule out: Remove().

Over high coupling, the only coupling that exists is between the context and its domain classes, which justly were made to shape the behavior of the context. That is, even if the coupling is reduced using interfaces, this generation of interfaces is useless because Models only make sense when used together with the context.

On the principle of Liskov’s replacement, a simple interface extraction of DbSet shows that we can implement a repository the way we want without prejudice to functionalities. There are other losses such as loss of SQL generation by filter extension methods, but implementation is possible. There is only one caveat here: Microsoft itself has not respected this principle because DbSet has a much richer implementation than IDbSet in the matter of methods.

On the Open/Closed Principle, Entity Framework supports inheritance and composition. Almost all methods that interact with DbSet and DbContext are extensions, so replacing them is very simple by simply extracting the interface and rewriting the database search method.

On the Principle of Single Liability, DbContext is an observer of DbSets, and just that, and DbSet is a collection of records of a particular entity with the 4 basic operations of a database: select, insert, edit and delete.

On the Dependency Inversion Principle, this can be solved by injection of dependency, but only if the programming team makes much question, because a DbContext shall exist only during the cycle of a request and therefore instantiated with it and deleted with it.

In conclusion, the Entity Framework does not hurt SOLID, with the exception of DbSet. Does not hurt OO principles. Does not cause high coupling.

Classic Problems of Approach

Now that I have talked about the fallacies, I will talk about the problems that the use of repositories brings.

Paging

The answer below summarizes.

Pagination C# MVC Asp . NET

Anticipated Charge (Eager Load)

The lazy load (Lazy Load) is set by default in the Entity Framework. What happens is that some turn off the lazy load to define the load strategy in the repository:

public MeuContexto() : base("name=DefaultConnection")
{
    this.ContextOptions.LazyLoadingEnabled = false;
    OnContextCreated();
}

I don’t know the meaning of this. The correct thing would be for the programmer to say when the anticipated load will be used. This is done through the extension method Include or by native method Include.

The big difference here is that Include, in an object context IQueryable, is pre-compiled by Linq to generate an SQL sentence with JOIN, and not simply use an operation in memory.

Problem of the Highlighted Context

Assuming an application that uses a repository on top of Entity Framework and has a code like this:

var entidadeDependente = EntidadesDependentesRepositorio.Selecionar(...);
var entidade = EntidadesRepositorio.Selecionar(...);

entidade.EntidadesDependentes.Add(entidadeDependente);
EntidadesRepositorio.Salvar(entidade);

The most common problem is:

An Entity Object cannot be referenced by Multiple instances of Ientitychangetracker.

It’s called Problem of the Highlighted Context because two objects are being observed by different contexts, and the correct thing is to have only one data context per request. That is, the object entidadeDependente is in another context, detached from the context of the main object, entidade.

It’s just another example of the misuse of the Entity Framework, which acts as an object observer, monitoring its modifications and persisting when SaveChanges() is called.

The following survey has some results on this problem. There is also this research, where the problem is dealt with by other names.

Problem of Conflicting Objects

It is characterized by the following message:

Attaching an Entity of type 'Classetal' failed because Another Entity of the same type already has the same Primary key value. This can happen when using the 'Attach' method or Setting the state of an Entity to 'Unchanged' or 'Modified' if any entities in the Graph have Conflicting key values. This may be because some entities are new and have not yet Received database-generated key values. In this case use the 'Add' method or the 'Added' Entity state to track the Graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

Very often in projects that try to implement the DDD. It occurs when trying to attach an object that already exists to the context. A classic example of error is:

var entry = contexto.Entry(objeto); // Isto já faz o contexto observar o objeto. 
contexto.Entidades.Attach(objeto); // O erro ocorre aqui.

It can happen with the same object (asking the context to observe it twice) or with different objects, when they have the same primary key.

  • speaking of LazyLoadingEnabled, I have the little vice of setting the this.Configuration.AutoDetectChangesEnabled = false and override the SaveChanges() this.ChangeTracker.DetectChanges(); return base.SaveChanges();

  • Quite dangerous this, by the way. Better trust in context and use good practices. Avoids a lot of problems.

  • I’ve had performance issues in previous versions of EF, for some reason he was calling the DetectChanges when accessing which property, disabling the AutoDetectChangesEnabled had a considerable great performance.

  • It should be version 5. I only feel these performance problems working with thousands of records, because the context of the Entity Framework was not made to work with changes of thousands of records at each request.

  • 1

    Yes I understand, really it was the case, basically it was a service that received files from different sources with different formats, and had to normalize the same, and due to some obstacles I could not use Bulkinsert, and as a result I ended up developing this addiction, now is to seek a treatment.

  • Being for Bulk Insert It is OK to use this approach, including version 6 up. I believe your system has some additional validation device. The problem is turning off the AutoDetectChangesEnabled at all times.

  • 2

    (1/2) Nice answer. You see some other option besides using Extension that gives a similar result? I still find it risky in the overly complex systems with which I have dealt to repeat queries in various places, because the rules of how to represent a state of the entity change, and along the development of a new solution can change a lot, and having to change all consumers is not productive. Interesting your idea of encapsulating only the 20% needed, but 20% in a large system is already enough thing and an entity state that today is described with only 1 attribute of it...

  • 2

    (2/2)...tomorrow becomes more complex and it is necessary to read other entities to define this state. So that which was not encapsulated in repository because it was simple now will need to be encapsulated, and I will have to make changes in various places of my system. Finally, in my view your answer, although interesting, still does not solve the problem of complex systems and I particularly have not had the peace of working in a system of low complexity for more than 15 years. (I don’t think Resp is worth the -1 it just received. I wasn’t going to vote but now I’m going +1)

  • @Caffé Complexity depends very much on the team’s stance in criticizing architecture. Another way to avoid repetition is by using Helpers. I’ve talked about it in a few answers, but it’s not consensual to be accepted.

  • @Caffé About entity states, the Entity Framework works like this: with only one attribute. To work with more than that is to extrapolate what he proposes. Obviously it does not exist to solve all problems, but gracefully solves most of them. The others will always demand a solution with some tailoring. In my work experience, using EF took a lot of effort and allowed me to think about what is important, which is the human production of the system.

  • Note that I do not mean the complexity of architecture or technology, but rather business rules. A complex domain will always result in complex modeling, which needs to be carefully designed and extensively refactored. I have seen that expressing a rule in a method name in a repository is advantageous in these complex domains. Please see in this answer a real example (although far from being my most complex example) of entity state that demands the interpretation of a set of attributes of it and other entities: http://answall.com/a/51439

  • @Caffé But then, the mechanism that the Entity Framework works to validate the relations is based on two devices: Model as IValidatableObject and the relationships described between entities by attribute decoration and by navigation properties. Expressing business rules in a repository falls into the same factorization problem of the repository itself that you yourself commented on.

  • Note that in that example I am not talking about validation but about the rule of obtaining entities ("bring the equipment that is in operation" and what defines that an equipment is in operation is not only 1 or 2 attributes of the entity). But you talking about validation has brought out another benefit of Reset, because although most validations belong to the entity itself, there are business validations that the entity should not know and these belong to the repository; and once again, when the validation rules external to the entity change, I change only the repository (a single place to change).

  • @Caffé But then: the repository is the place to harmonize entities? I don’t think. I think these validations between entities need to be done by a BL, service layer or, in the case of MVC, by Controller.

  • 2

    Validations between entities can be business rules (usually they are). If these rules are in the Controller and I need them elsewhere, like during a business process or even to expose a service to an external system, what about? Do I replicate the Controller rules elsewhere? In my view no business rule of a system with the least complexity could reside in the Controller. 100% of the business rules must be encapsulated somewhere (we often call this place the "domain layer").

  • Mosh has a video just explaining when Repository Pattern is better than Dbcontext, starts at minute 5:04 and shares below: https://youtu.be/rtXpYpZdOzM?t=5m4s Two standards are asked: 1) Does not minimize duplication of logic queries (That you can live without) 2) Does not separate the application from a Framework change (For example, if you want to use Dapper in some performance-specific method)

  • @mqueirozcorreia This is the biggest conversation in history. You can use Dapper together with EF simply by using db.Database.Connection.Query(), because Dapper is a simple extension of IDbConnection. For duplication of darlings of logic, this is already explained in the answer when I talk about extension methods over DbSet static in scope (Helper). Does not proceed.

  • @Ciganomorrisonmendez without the use of repositories (Infra.Data.Repositories) the operations with the database would pass inside Command or would be directly on the layer of Solution.Application.Services adding the E.F reference ? I always used Repository Pattner and with this post I had this doubt, if I don’t have this "Layer" of Repository, where would the operations in the bank ?.. =)

  • @Jcsaint Why do you need a service layer? Do you have a Windows Forms and MVC application where you both share the same logic? Very few cases justify using this, but in reply, yes, the reference to RU would be in the service layer.

  • @Caffé I know that the question is very old and as the Gypsy currently does not participate so much in the SOP, you talked about searching records with complex rules or implementations of business rules. In your case how did you implement this? Using a Helper?

  • Starting with never, is the worst thing you could have done, because there is no silver bullet solution for computing. Another curious point is that you only spoke of get, but did not speak change data. Even worse is leaves the context "open" for misuse. Your case could only be applied in extremely simple case.

  • @Albertomonteiro Define "misuse". Define cases where this application is incorrect for changing data.

Show 18 more comments

17

There are actually some people who advocate using directly the Entity Framework instead of using a repository. I’m going to expose here some motivations that I believe are valid for actually using repositories.

Setting up a repository doesn’t take much work. Basically you create an interface with the methods you need to manage the life cycle of entities and then code everything in the implementation of the same, in another project.

Some people believe that if you have an initially very simple application it is an exaggeration to use repositories, but as the work to implement this pattern is very little, I usually use it.

Some advantages, in my view, when we use repositories are:

Flexibility in how to deal with persistence

Using a repository gives you flexibility in the way you handle persistence. What I mean is the following: for some of your persistence operations you can use EF, for others you can use ADO.NET and optimize the query. Not to mention "load or not associate entities" issues. You may still want to use along with EF other technologies like Dapper, or etc. A repository allows you to leave all this in one place and follow the DRY principle (don’t repeat yourself).

Use in domain service

Imagine that there is a business logic that deals with more than one entity and is naturally not part of any of them as a method. In this case, you usually build a domain service. If you use repositories and have an interface that is part of the domain, your domain service can use the repository without relying on technology.

If you use EF directly, the only way to do this would be to place EF calls on your domain, which would pollute it with implementation technology and detail.

Facility to exchange the ORM if necessary

Finally, today you use EF, but maybe tomorrow for some reason you might need to trade. Maybe you switch to Nhibernate, or maybe there’s some other ORM that’s better for your application for some reason. If you have spread the EF code everywhere, you will have to change in a lot of place if you have a repository will change in one place.

  • 1

    Interesting points, the question of flexibility is interesting, my only drawback here, would be that the use of ADO.NET to perform optimizations, in this case I believe that the call of Stored Procedures by the ORM generates a much smaller disorder if it is necessary to exchange the Database. The issue of the dr domain service, I prefer not to comment because it is not my domain. Regarding the change of ORM, I believe that this will only occur due to a very bad decision when choosing the ORM, I believe that a change of DB for financial reasons (ex: Oracle to Postgree) is much more common.

  • Cool the answer.

  • 1

    I did a study yesterday on the subject and would only add one more benefit: Minimizes duplication of query logic(query). Then instead of exposing a Iqueryable, one exports methods that make queries with filters. I saw this in the Mosh channel and did the post http://apps web.blogspot.com.br/2017/03/padrao-de-repositorio-no-c.html

3

There is no definitive answer to that. Some argue that the Entityframework is the repository itself, (as in the answer that was marked as correct) others say that the Entity Framework does not implement a repository as it should, as you can see in this video (it’s in English) Repository Pattern with C# and Entity Framework, Done Right

Personally I prefer to always use the Repository standard. As a practical example, I have a system that was initially developed using ADO.NET with Repository standard, in the future I intend to exchange for Entity Framework and, as everything is in the repository, it is much easier to make this change.

I could happen to have developed directly with Entity Framework and want to switch to ADO.NET for performance reasons. When in doubt I prefer to have all logic separated into repositories, even using Orms.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.