Why repository pattern is extensively used in entity framework as though it is complex?
Asked Answered
P

6

10

I am creating a demo project which contains crud operations using a repository pattern and dependency injection.

This is my structure:

Approach 1 (very popular, used by many developers)

My repository interface:

public partial interface IRepository<T>
{
    void Insert(T entity);
}

My service layer:

public partial interface IEmployeeService
{
    void InsertCategory(EmployeeMaster employeeMaster);
}

My class which will implement that interface (service):

public partial class EmployeeService : IEmployeeService
{
    private readonly IRepository<EmployeeMaster> _employeeMasterRepository;

    public EmployeeService(IRepository<EmployeeMaster> employeeMasterRepository)
    {
         this._employeeMasterRepository = employeeMasterRepository;
    }

   public virtual void InsertCategory(EmployeeMaster employeeMaster)
   {
        _employeeMasterRepository.Insert(employeeMaster);
   } 

This is my controller:

public class HomeController : Controller
{
        private readonly IEmployeeService  _employeeService;

        public HomeController(IEmployeeService employeeService)
        {
            this._employeeService = employeeService;
        }

Above is the structure which I am following from Nop Commerce project (http://www.nopcommerce.com) and I think most developers now a days are following this structure only that is repository pattern and dependency injection.

Previously I was doing like Below like making one Bal(business Access layer) project in my application and then making class file in Bal and doing insert like below:

Approach 2:

public class MyBal()
{
    public void Insert()
    {
        using (MyEntities context = new MyEntities ())
        {
            var result = context.MyTable.Add(_MyTable);
            context.SaveChanges();
            return result;
        }
}

And then Calling this Method straight Away from My Mvc application like this:

[HttpPost]
public ActionResult Insert(Model M)
{
    MyBal bal = new MyBal ();
    bal.Insert();
}

So why most of the developers go on creating such a complex structure that is repository pattern.Can anybody explain me the difference between Approach 1 and Approach 2 ??

Does Approach 1 increases performance or it is just related to separation of code or better maintainability of code.

Prudi answered 27/2, 2015 at 5:46 Comment(3)
please note this question could be construed as Primarily Opinion Based, and may be subject to closure. However, I felt the topic important enough to make an attempt at an answer. I have, however, stated in my answer that this is all subject to opinion.Frisk
@Frisk I dont think its subjective at all. Clearly in Approach one is using DI and Approach 2 is not. Op: In Approach 2 you have a hidden dependency on MyBal which is the critical difference.Hagiarchy
This question isn't "opinion based" at all - it questions exactly what impact SOLID, abstraction and unit testing has on the fundamental process of engineering software. Yes you could do it using approach 2, but as soon as you question "how am I going to test this?" and "why does a VIEW controller need to know anything about the database technology that stores data in my system?" the entire engineer approach changes. That's a perfectly valid (empirical) question.Journeywork
H
5

To provide a code example of why Approach 1 is better consider a test scenario. With Approach 2, when you call the Insert method in a unit test you will actually be calling:

Controller.Insert > MyBal.Insert > Database execution.

This is not what we want in unit testing. In unit testing we just want to test the unit (code block) not the object graph of it's entire call stack.

This is because you have hardcoded a dependency on MyBal and there is no way to switch it out. If we used Approach 1 however we might have:

public class HomeController : Controller
{
        private readonly IMyBalService  _ibalService;

        public HomeController(IMyBalService ibalService)
        {
            _ibalService = ibalService;
        }

        public ActionResult Insert(Model M)
        {
           ibalService.Insert();
        }
 }

Now you can have your MyBal class implement IBal interface:

public class MyBal : IBalService
{
    public void Insert()
    {
        using (MyEntities context = new MyEntities ())
        {
            var result = context.MyTable.Add(_MyTable);
            context.SaveChanges();
            return result;
        }
}

And under production scenario all would work as it currently does. BUT under test now you can also make an BalMockService.

public class BalMockService : IBalService
{
    public void Insert() {}
}

And you can pass that into your HomeController when testing. Notice how we removed all the database calls? They are not needed for testing Controller.Insert. Alternatively we could have made BalMockService return some test data, but you have a void so it's not needed.

The point is we've decoupled HomeController from MyBal and we've also advertised the fact the HomeController requires an IBalService of some kind to run.

Hagiarchy answered 27/2, 2015 at 7:30 Comment(2)
so their is no performance issue naa in both the cases only maintainance of code and unit ot testing is good.thats it naPrudi
@MariaPithia Well there could be performance gains in using Approach 1 if MyBal was a really expensive type to create. With Approach 2, you are creating one everytime the Home.Insert action is hit. Using Approach 1, you might be able to create one instance of MyBal once, when the app starts and then just keep passing it in to HomeController and whereever else it's needed. Depends on the types involved but yes that can be another advantage so for example some kind of cache.Hagiarchy
F
12

Many people implement a Repository on top of Entity Framework, which in itself uses the Repository Pattern. There are a few variations on reasoning behind this; the decision as to if any of these make sense is a matter of opinion.

  1. It is the way Microsoft demonstrated in their tutorial series, Part 9 of 10. Because Microsoft demonstrated this pattern, it is widely accepted as a reasonable practice.

  2. Separation from Entity Framework. Many people strive to separate Entity Framework from their business entities, with the idea that if they ever choose to replace Entity Framework, they can change the repository without modifying other business logic. In practice, however, you should really commit to a technology, and it's quite a bit of work to correctly separate the Entity Framework logic. In the end, you most likely end up with methods in your repository that only exist because it's the EF way of doing things, so changing to another ORM is still going to affect your business entities.

  3. Generics. It is a very common practice to use Generics with the Repository Pattern, which minimize code repetition. The thought is that if you have hundreds of classes to perform CRUD operations with, a Generic Repository will be more uniform and more maintainable, but with possibly a bit less flexibility.

  4. Ease of integration with Dependency Injection tools. It may be easier in some cases to work with Dependency Injection tools by using the Repository Pattern.

  5. Unit Testing. This goes along with point 4, using the Repository Pattern gives you a uniform class which can easily be mocked for Unit Tests.

This is by no means an exhaustive list, and there are just as many reasons to disagree with each point as to agree with them, but this is my observations of the thought processes around this particular pattern.

As for your second question, the difference between the two examples... In the first case, you have created a service and a repository, which may exist in an entirely different project or namespace from your business entity. Your business entity doesn't know about, or care about, Entity Framework. If you need to change the way that your Repository works, it should have little to no impact upon your entity, which will operate the same way it always has.

In your second case, you are directly tied to Entity Framework. This, and any other business entity must know about Entity Framework, and any changes to Entity Framework could potentially mean changing code in this or any other entity like it. With many entities, this could be a very lengthy process. Also, you cannot easily test this entity, because any tests you perform are going to cause writes to the database.

Frisk answered 27/2, 2015 at 6:9 Comment(5)
Sir you didnt explained me the difference between the 1st and 2nd approach.i want to know the difference between the two in technical termsPrudi
I'm not sure how much more technical I can be in answering a question titled "Why repository pattern is extensively used in entity framework...".Frisk
It is not unfortunately at all. If you are using poco classes with EF then I actually create the domain objects in a seperate domain level project.Tarbox
@Claies:sir but still if you can explain me the difference between two??Prudi
@Claies:thank you so much sir for an excellent awesome answer and explainationPrudi
H
5

To provide a code example of why Approach 1 is better consider a test scenario. With Approach 2, when you call the Insert method in a unit test you will actually be calling:

Controller.Insert > MyBal.Insert > Database execution.

This is not what we want in unit testing. In unit testing we just want to test the unit (code block) not the object graph of it's entire call stack.

This is because you have hardcoded a dependency on MyBal and there is no way to switch it out. If we used Approach 1 however we might have:

public class HomeController : Controller
{
        private readonly IMyBalService  _ibalService;

        public HomeController(IMyBalService ibalService)
        {
            _ibalService = ibalService;
        }

        public ActionResult Insert(Model M)
        {
           ibalService.Insert();
        }
 }

Now you can have your MyBal class implement IBal interface:

public class MyBal : IBalService
{
    public void Insert()
    {
        using (MyEntities context = new MyEntities ())
        {
            var result = context.MyTable.Add(_MyTable);
            context.SaveChanges();
            return result;
        }
}

And under production scenario all would work as it currently does. BUT under test now you can also make an BalMockService.

public class BalMockService : IBalService
{
    public void Insert() {}
}

And you can pass that into your HomeController when testing. Notice how we removed all the database calls? They are not needed for testing Controller.Insert. Alternatively we could have made BalMockService return some test data, but you have a void so it's not needed.

The point is we've decoupled HomeController from MyBal and we've also advertised the fact the HomeController requires an IBalService of some kind to run.

Hagiarchy answered 27/2, 2015 at 7:30 Comment(2)
so their is no performance issue naa in both the cases only maintainance of code and unit ot testing is good.thats it naPrudi
@MariaPithia Well there could be performance gains in using Approach 1 if MyBal was a really expensive type to create. With Approach 2, you are creating one everytime the Home.Insert action is hit. Using Approach 1, you might be able to create one instance of MyBal once, when the app starts and then just keep passing it in to HomeController and whereever else it's needed. Depends on the types involved but yes that can be another advantage so for example some kind of cache.Hagiarchy
L
4

Yes. The answer is better and more maintainable code.

The full answer is more complicated.

Having said that it's better and more maintainable code, the benefit of Repository/Service pattern is still debatable, and it's not the only pattern available.

Approaches to problems are sometimes more complex because the problems are more complex.

While learning about design patterns, I often wonder "Why is this code so complicated? Why are they doing it this way?".

The problem is that they are applying complex solutions to a simple problem, because they're demonstrating how to use it (the design pattern). But without a complex problem, it's difficult to see why the design pattern is beneficial.

Your second approach, while being simpler and more readable, increases coupling and will be harder to maintain if the application becomes much larger.

Liatrice answered 27/2, 2015 at 6:1 Comment(4)
How Approach 2 increases coupling and how it is harder to maintain.could you please explain me that??Prudi
@MariaPithia Approach 2 increases coupling because you creating an instance of a concrete type MyBal. Approach 1 is merely passing in an interface which means you can pass in ANY type that implements that interface. Approach 2 is also doubly bad because you are creating the service MyBal within the ActionMethod. This violates the Single Responsibility Principle and also hides the dependency of your controller on the MyBal service. So how do you now test that method in isolation? You cant. To test the Insert method you are now also testing MyBal which in turn means testing into all your EF codeHagiarchy
@rism:what is Single Responsibility Principle??Prudi
Well thats another question ;) Which you can answer by Googling "SOLID programming". Simply put a class should have one and only one reason to change. By embedding a hidden dependency on MyBal into HomeController you now have 2 potential reasons to change. 1 if HomeController needs to change which is ok but 2: if MyBal needs to change which is not ok.Hagiarchy
S
1

For more maintainable code, the looser coupling provided by using an interface and dependency injection is generally helpful. More specifically, they help a lot when doing unit testing -- one of the things that draws some developers to an MVC framework in the first place. See: What is dependency injection?

Soilure answered 27/2, 2015 at 6:15 Comment(0)
T
1

There is a good book about domain driven design, and also in there the repository pattern shows up. It is a good design pattern.

ref http://books.google.be/books/about/Domain_Driven_Design.html?id=hHBf4YxMnWMC&redir_esc=y

In that book the repository is used for the aggregate root. The idea around this pattern is to have better separation of concerns and to follow SOLID design principles. The repository pattern has a single responsibility.

Tarbox answered 27/2, 2015 at 6:58 Comment(0)
B
1

Generally speaking Approach 1 is better than approach 2. Reasons are correctly stated by Clais and Rowan.

I would like to share another approach quite similar to Approach 1 but a more generic version of it.

It simplifies the design and implementation immensely. Basically it defers from Approach 1 in "not having specific multiple repository" but "have just one".

If you are in research phase you may want to consider it. I have explained it with code sample in this answer.

Boling answered 27/2, 2015 at 7:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.