0

I'm using an EF Code First approach with an ASP.NET MVC 3 application, and instead of re-creating the wheel, I was wondering if there already exists a solid base Repository class that my custom Repository classes could extend in order to provide default functionality out of the box (e.g. basic CRUD, etc...).

So something like this ...

public class CustomerRepository : BaseRepository { ... }

... would therefore provide a default way to work with Customer objects out of the box. I'd like to then inject an ICustomerRepository into my MVC controllers and have the functionality available to me there.

I'm sure something like this already exists out there as I've done something similar with NHibernate.

Thanks

wgpubs
  • 8,131
  • 15
  • 62
  • 109
  • In my opinion `IQueryable` is your repository: http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=84. – Steven Jul 13 '12 at 17:03

4 Answers4

5

No, there is no built-in repository, other than EF itself (which is in and of itself an implementation of the Unit of Work pattern, and DbSet's are basically Repositories).

There is currently a debate in the software community over whether generic repositories have much real value. For testing purposes, many argue, they provide easy unit testing. Others say that unit testing repositories doesn't help because mocked repositories don't behave the same way that real ones do (because of the linq -> Sql translation layer, which doesn't exist in a mocked repository).

Many are suggesting that you do integration testing against EF using an in-memory database like SqlLite rather than unit testing it.

Still, if you are intent on using repositories, there are many examples out there on the net, with varying styles and methods. Or you can roll your own. MS does not provide one.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
3

In my experience, write your own repositories is redundant because EF implements this pattern already through DbSet's.

I worked with MVC3 + EF Code Fisrt in a recent project. We started implementing a generic repository following some tutorials and soon we realized that we are writing a lot of unnecessary and redundant code. Actually, the repositories were given us nothing but hiding a lot of the DbSet's functionality. Finally, we decided to remove them and work with our DbContext and DbSet's directly.

But, how about complex business logic beyond simple CRUD operations?

Well, we exposed all complex functionality like queries and multiple CRUD operations through a service layer. You can build different service classes by functionality. By example, you can write an AccountService to manage all functionality related with user accounts. Something like this:

public class AccountService {

    private MyContext ctx;

    public AccountService(DbContext dbContext) {
        this.ctx = (MyContext)dbContext;
    }

    /// <summary>
    /// Gets the underlying DbContext object.
    /// </summary>
    public DbContext DbContext {
        get { return ctx; }
    }

    /// <summary>
    /// Gets the users repository.
    /// </summary>
    public DbSet<User> Users {
        get {return ctx.Users;}
    }

    public bool ValidateLogin(string username, string password) {
        return ctx.Users.Any(u => u.Username == username && u.Password == password);
    }

    public string[] GetRolesForUser(string username) {
        var qry = from u in ctx.Users
                  from r in u.Roles
                  where u.Username == username
                  select r.Code;

        return qry.ToArray<String>();
    }

    public User CreateUser(string username, string password) {
        if (String.IsNullOrWhiteSpace(username)) throw new ArgumentException("Invalid user name");
        if (String.IsNullOrWhiteSpace(password)) throw new ArgumentException("Invalid password");

        User u = new User {
            Username = username.Trim().ToLower(),
            Password = password.Trim().ToLower(),              
            Roles = new List<Role>()
        };

        ctx.Users.Add(u);
        ctx.SaveChanges();

        return u;
    }

How about dependency injection?

Using this approach, the only thing we need to inject is the DbContext. The service classes has a constructor that takes a DbContext. So, when your controller constructor takes a service instance the DbContext will be injected to it.

Edit: Example code
This is an example code about how you controller could look:

public class HomeController : Controller {

    private readonly AccountService accountService; 

    public AccountController(AccountService accountService) {
        this.accountService = accountService;
    }
}

And this could be the DI configuration using NInject:

private static void RegisterServices(IKernel kernel) {
    kernel.Bind<MyContext>().ToSelf().InRequestScope();
    kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<MyContext>());
}

How about unit testing?

You could build specific interfaces for each service layer class and mock it where you need.

fcaldera
  • 584
  • 1
  • 6
  • 18
  • Can you explain how your controller constructor looks with regards to DI? For example, lets say I need "MyContext" and "AccountService" in my HomeController ... would I set up the controller's constructor like this: "public HomeController(MyContext ctx, AccountService acctSvc) { ... }" – wgpubs Jul 13 '12 at 06:47
  • Also, assuming I understand you right, what is the point of passing a DbContext instead of simply passing MyContext as the first argument to your AccountService controller? Using "MyContext" I can set up my controller as such: public HomeController(MyContext ctx, AccountService acctSvc) ... AND THEN using Autofac, for example, I can do: builder.RegisterType().InstancePerHttpRequest(); builder.RegisterType().InstancePerHttpRequest(); The benefit here is that the MyContext created via Autofac will be used as an argument to AccountService. Sound right??? Thanks – wgpubs Jul 13 '12 at 08:04
  • About your first question, I just put an example code. You would not need include "MyContext" in your controller constructor since you can access to it through accountService.DbContext. – fcaldera Jul 13 '12 at 15:03
  • Second: Yes off course, you can use a "MyContext" as argument of the "AccountService" controller instead of a "DbContext". I'm just trying to implement the code as generic as possible, because I don't need call specific methods of MyContext into my controllers. As you note we are exposing the repositories through the service, but only that ones the service is related. This help you to encapsulte some fucntionality. – fcaldera Jul 13 '12 at 15:15
  • 1
    There are a few issues with your implementation. First, you're exposing the Data layer directly to the UI (ie your Users property). This has certain security issues you need to be aware of, but so long as you don't directly use Entities as your View Models, that should be ok (you still need separate view and business objects). I know, a lot of people think this is redundant, but View Models are part of separation of concerns and should be separate from your data entities. – Erik Funkenbusch Jul 17 '12 at 12:18
0

A friend of mine, Sacha Barber wrote a nice article covering some of these ideas.

Link can be found here.

RESTful WCF / EF POCO / Unit of Work / Repository / MEF: 1 of 2

stevethethread
  • 2,524
  • 2
  • 30
  • 29
0

EF has a base class called DbContext. You can add properties of type DbSet<TEntity>

This allows you to do something like this:

public class User {
    public int Id { get; set; }
    public string Name { get; set; }
}

public class DatabaseContext : DbContext {
    public DbSet<User> Users { get; set; }
}

You can now use this like so:

using(var db = new DatabaseContext()) {
    User jon = new User {Name = "Jon Smith"};
    db.Users.Add(jon);
    db.SaveChanges();
    var jonById = db.Users.Single(x => x.Id == 1);
}

If you want more abstraction see this post about building a generic repository around EF Entity Framework 4 CTP 4 / CTP 5 Generic Repository Pattern and Unit Testable Just to note, this level of abstraction is not always needed. You should decide if your abblication will truly benefit from adding a generic repository over just using DbContext directly.

Community
  • 1
  • 1
Paul
  • 12,392
  • 4
  • 48
  • 58