Reputation:
Recently i've working on an ASP.NET MVC5 project, i dived right in and wrote all my logic right in the action method and after doing this for a few controllers i've noticed that i have been duplicating certain business rules and could do with being lifted out and shared between controllers.
From what i've read, the m in asp.net mvc is a layer consisting of entities, viewmodels and services, the latter holding all your shared business logic
now i'm trying to keep things as simple as possible, i don't want to wrap entity framework in some UoW/Repo and use it as-is, it is very unlikely that i'll stop using entity framework in this applications lifetime and i'm not doing unit tests and i'm not that bothered about tight coupling, so i don't feel i need an IoC container, but all the tutorials i've read seems to use either an IoC container or wraps dbcontext/ef in a UoW/Repo.
I've read that there should only be a single instance (which in the tutorials i've seen is managed via an IoC container) of DbContext per httprequest, would this be achieved by instantiating it in the controllers constructor and then passing that reference to any services needed in the controller and then disposing it at the end of the request? is this the correct way of managing dbcontext?
Controller example:
public class SupplierController : Controller
{
private Meerkat3Context context;
private SupplierService supplierService;
private ratingService SupplierRatingService;
public SupplierController()
{
// instantiate the dbcontext
this.context = new Meerkat3Context();
// pass dbcontext into the constructors of my services
this.supplierService = New SupplierService(context);
this.ratingService = New SupplierRatingService(context);
}
public ActionResult Index(Guid id)
{
var supplier = supplierService.getSupplier(id);
// construct viewmodel
return new SupplierIndexViewModel()
{
SupplierId = supplier.Id,
SupplierName = supplier.Name,
SupplierRating = ratingService.getHighestRating(supplier.Id),
NearbySuppliers = supplierService.getNearbySuppliers(supplier.Id),
// etc
};
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
context.Dispose();
}
base.Dispose(disposing);
}
}
Service examples:
public class SupplierService
{
private Meerkat3Context context;
public SupplierService(Meerkat3Context context)
{
this.context = context;
}
public Supplier getSupplier(Guid id)
{
return context.Where(x => x.SupplierId == id)
.FirstOrDefault()
.Select(x => new Supplier()
{
Id = x.Id,
Name = x.Name
// etc
});
}
public Supplier getNearbySuppliers(Guid id)
{
return context.Suppliers.Where(x => context.SupplierAddresses
.Where(y => y.AddressTypeId == AddressTypes.Location)
.Select(z => z.Address.TownCity)
.Contains(x.SupplierAddresses
.Where(y => y.AddressTypeId == AddressTypes.Location)
.FirstOrDefault()
.Address.TownCity)
);
}
}
public class SupplierRatingService
{
private Meerkat3Context context;
public RatingService(Meerkat3Context context)
{
this.context = context;
}
public SupplierRating getHighestRating(Guid id)
{
return context.SupplierRating
.Where(x => x.SupplierId == id)
.OrderBy(x => x.RatingValue)
.FirstOrDefault()
}
}
Upvotes: 1
Views: 2060
Reputation: 15663
With an IoC container your controller would look like.
public class SupplierController : Controller
{
//the controller doesn't need to create the db context now
//this concern is handled now by the IoC container
private SupplierService supplierService;
private RatingService SupplierRatingService;
public SupplierController(SupplierService supplierService, RatingService ratingService)
{
// we don't have to pass the db context now to services, since we retrieve the services from the IoC container. The IoC container auto-wires the services
this.supplierService = supplierService;
this.ratingService = ratingService;
}
public ActionResult Index(Guid id)
{
var supplier = supplierService.getSupplier(id);
// construct viewmodel
return new SupplierIndexViewModel()
{
SupplierId = supplier.Id,
SupplierName = supplier.Name,
SupplierRating = ratingService.getHighestRating(supplier.Id),
NearbySuppliers = supplierService.getNearbySuppliers(supplier.Id),
// etc
};
}
// the controller doesn't need a dispose method since the IoC container will dispose the dbcontext for us
}
You don't have to follow the Dependency Inversion Principle to use an IoC container, but you can count on a IoC container to create and to manage the lifetime of your services objects.
You configure the IoC container to create a single instance of a dbcontext per a web request. The good part is this is configurable and, if you later decide is better to have a different dbcontext instance per service, then you just change this in a single place and not in every controller and every action method where you use the new keyword.
Upvotes: 0
Reputation: 718
If you're trying to strip out the repeated code, this should be fairly simple. In VS you can highlight a section of code and use the hotkeys Ctrl+R,Ctrl+M
for refactor, or you can do so by using the context menu highlight code section > right-click > Refactor > Extract Method
.
If the usage of the repeated code can be replicated for all entities, you can create a static class that houses this common functionality.
public sealed class Utlities
{
public static CommonA() { }
public static CommonB() { }
... etc...
}
And you can call them easily using Utilities.CommonA()
. Another way to reduce redundancy is to use ViewModels. Basically create a copy of the entity you want to use as a ViewModel with additional properties required for the View. If the models have data in common, create a base class to inherit those commonalities from.
public class BaseViewModel
{
public Type Prop {get; set;}
public Type Prop2 {get; set;}
...etc...
}
public class SpecificViewModel : BaseViewModel
{
SpecificViewModel(Type Prop, Type Prop2) : base(Prop, Prop2, ...etc...) { }
public Type specificProp {get; set;}
...etc...
}
If I understood your question correctly that is.
Upvotes: 1
Reputation: 5832
If what you want is simply moving out reusable logic then your approach is good enough. But please bear in mind that:
it isn't testable (you can't isolate your dependencies and
You're still duplicating the logic, even if it's simply an object construction logic (e.g., in every controller where you need SupplierService
you'll have to instantiate Meerkat3Context
as well). That can get quite tedious (and that's where DI comes in handy)
Upvotes: 0