Skip to main content
Added example of service layer
Source Link
dreza
  • 6.5k
  • 2
  • 30
  • 42

As requested here's an example of abstracting away EF as much as I was able whilst not requiring a seperate repository layer and using a service type approach instead (don't sue me for spelling errors :))

NOTE: Example was using Code first methodology of Entity Framework.

public interface IDataContext { IDbSet<Address> Addresses { get; set; } IDbSet<Contact> Contacts { get; set; } System.Data.Entity.Database Database { get; } DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class; int SaveChanges(); } public class MyDataContext : DbContext, IDataContext { public IDbSet<Address> Addresses { get; set; } public IDbSet<Contact> Contacts { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); } } 

Using a service layer is primarily where I will now store my business logic

public class BaseService { protected readonly IDataContext DataContext; public BaseService(IDataContext dataContext) { DataContext = dataContext; } } 

Potentially for mocking I might consider creating an interface for each service I create

public interface IAddressService { IEnumerable<Address> All(int id); Address GetById(int id); IEnumerable<Address> GetAddressWithinRange(string street, int rangeInMetres); } public class AddressService : BaseService, IAddressService { public AnalogService(IG5DataContext dataContext) : base(dataContext) { } public IEnumerable<Address> All() { return DataContext.Addresses.Where(p => !p.Deleted).ToList(); } public Address GetById(int id) { return DataContext.AnalogInputs.Find(id); } public IEnumerable<Address> GetAddressWithinRange(string street, int rangeInMetres) { return DataContext.Addresses .AsQueryable() .Where(p => p.Street == street && p.DistanceFromCentre < rangeInMetres); .ToList(); } // Other business methods here private IQueryable<Address> AsQueryable(Machinery machinery) { return DataContext.Entry(machinery).Collection(v => v.AnalogReadings).Query(); } } 

Then using an IOC container (Ninject, AutoFac, Unity are a few I've used. Or see this blog by Scott Hanselman for a list of what's around) I would inject these into my controller. The setup of the dependency registration would be dependant on the IOC implementation.

public class AddressController { private readonly IDataContext _dataContext; private readonly IAddressService _addressService; public AddressController(IDataContext dataContext, IAddressService addressService) { _dataContext = dataContext; _addressService = addressService; } [HttpGet] public ActionResult Index() { var addresses = _addressService.All(); return View("Index", addresses); } [HttpGet] public ActionResult Details(int addressId) { var address = _addressService.GetById(id); if(address == null) return HttpNotFound(); return View("Address", address); } [HttpPost] public ActionResult Details(Address address) { var model = _addressService.GetById(address.Id); if(address == null) return HttpNotFound(); // I might consider using A mapping framework like AutoMapper here. Not sure if this is correct // syntax but hopefully you get the point Mapper.Map(address, model); _dataContext.SaveChanges(); return RedirectToAction("Details"); } } 

As requested here's an example of abstracting away EF as much as I was able whilst not requiring a seperate repository layer and using a service type approach instead (don't sue me for spelling errors :))

NOTE: Example was using Code first methodology of Entity Framework.

public interface IDataContext { IDbSet<Address> Addresses { get; set; } IDbSet<Contact> Contacts { get; set; } System.Data.Entity.Database Database { get; } DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class; int SaveChanges(); } public class MyDataContext : DbContext, IDataContext { public IDbSet<Address> Addresses { get; set; } public IDbSet<Contact> Contacts { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); } } 

Using a service layer is primarily where I will now store my business logic

public class BaseService { protected readonly IDataContext DataContext; public BaseService(IDataContext dataContext) { DataContext = dataContext; } } 

Potentially for mocking I might consider creating an interface for each service I create

public interface IAddressService { IEnumerable<Address> All(int id); Address GetById(int id); IEnumerable<Address> GetAddressWithinRange(string street, int rangeInMetres); } public class AddressService : BaseService, IAddressService { public AnalogService(IG5DataContext dataContext) : base(dataContext) { } public IEnumerable<Address> All() { return DataContext.Addresses.Where(p => !p.Deleted).ToList(); } public Address GetById(int id) { return DataContext.AnalogInputs.Find(id); } public IEnumerable<Address> GetAddressWithinRange(string street, int rangeInMetres) { return DataContext.Addresses .AsQueryable() .Where(p => p.Street == street && p.DistanceFromCentre < rangeInMetres); .ToList(); } // Other business methods here private IQueryable<Address> AsQueryable(Machinery machinery) { return DataContext.Entry(machinery).Collection(v => v.AnalogReadings).Query(); } } 

Then using an IOC container (Ninject, AutoFac, Unity are a few I've used. Or see this blog by Scott Hanselman for a list of what's around) I would inject these into my controller. The setup of the dependency registration would be dependant on the IOC implementation.

public class AddressController { private readonly IDataContext _dataContext; private readonly IAddressService _addressService; public AddressController(IDataContext dataContext, IAddressService addressService) { _dataContext = dataContext; _addressService = addressService; } [HttpGet] public ActionResult Index() { var addresses = _addressService.All(); return View("Index", addresses); } [HttpGet] public ActionResult Details(int addressId) { var address = _addressService.GetById(id); if(address == null) return HttpNotFound(); return View("Address", address); } [HttpPost] public ActionResult Details(Address address) { var model = _addressService.GetById(address.Id); if(address == null) return HttpNotFound(); // I might consider using A mapping framework like AutoMapper here. Not sure if this is correct // syntax but hopefully you get the point Mapper.Map(address, model); _dataContext.SaveChanges(); return RedirectToAction("Details"); } } 
Source Link
dreza
  • 6.5k
  • 2
  • 30
  • 42

I've used this kind of approach myself in the past and it's worked well. However lately I've been considering whether it's overkill. Considering Entity framework is already following the repository and unit of work pattern you are essentially just adding the same pattern over the same pattern and that may potentially not give you any extra benefit.

I've tended to find myself using more of a service architecture while still using dependency injection etc to manage the dependencies into my various classes. I've found it's meant less layers without losing any benefits of abstraction and TDD. It will still allow you to decouple your business logic from the data access whilst not having that extra layer of complexity.

I'm also not sure about enforcing all your entities to requiring an Id. There are plenty of situations when Id's are not required on model table definitions so to enforce such a restriction seems to me potentially limiting? Situations such as link tables, or where a table might have a composite primary key rather than just an Id field.

In saying all that, nice implementation. Seems to follow conventions of the patterns you are hoping to achieve.