MVC 3 - how to implement a service layer, do I need repositories?

萝らか妹 提交于 2019-11-29 21:34:29

You've got the general idea, but it takes a while to really get used to Dependency Injection. I see a number of possible improvements to be made:

  1. Your IServices interface seems unnecessary. I'd prefer to have the controller specify which services it needs (IPhotoService, etc.) via its constructor, rather than using the IServices interface like some kind of strongly-typed service locator.
  2. Did I see a DateTime.Now in there? How are you going to verify that the date gets set correctly in a unit test? What if you decide to support multiple time zones later? How about using an injected date service to produce that CreatedDate?
  3. There is a very good Ninject extension specifically for MVC. It takes care of plugging into the various points that MVC 3 supports for injection. It implements things like your NinjectControllerFactory. All you have to do is make your Global class extend a specific Ninject-based application.
  4. I'd suggest using NinjectModules for setting your bindings, rather than setting them in your ControllerFactory.
  5. Consider using Binding by Convention so that you don't have to explicitly bind each service to its implementation.

Update

The Ninject MVC Extension can be found here. See the README section for an example of how to extend the NinjectHttpApplication. This example uses Modules, which you can read more about here. (They're basically just a place to put your binding code so that you don't violate the Single Responsibility Principle.)

Regarding conventions-based bindings, the general idea is to have your binding code scan the appropriate assemblies and automatically bind things like IPhotoService to PhotoService based on the naming convention. There is another extension here to help with such things. With it, you can put code like this in your module:

Kernel.Scan(s =>
                {
                   s.From(assembly);
                   s.BindWithDefaultConventions();
                });

The above code will auto-bind every class in the given assembly to any interface it implements that follows the "Default" conventions (e.g. Bind<IPhotoService>().To<PhotoService>()).

Update 2

Regarding using the same DbContext for an entire request, you can do something like this (using the Ninject.Web.Common library, which is required by the MVC extension):

Bind<SiteContext>().ToSelf().InRequestScope();

Then any context-dependent services that Ninject creates will share the same instance across a request. Note that I have personally used shorter-lived contexts, so I don't know off the top of my head how you'd force the context to be disposed at the end of the request, but I'm sure it wouldn't be too difficult.

The IServices and Services types seem superfluous to me. If you drop them and change your controller's constructor to be

public PhotoController(IPhotoService photoService, IProfileService profileService)
{
  _photoService = photoService;
  _profileService = profileService;
}

it will be more apparent what it is actually depending on. Moreover, when you create a new controller, that only really needs IProfileService, you can just pass an IProfileService instead of a full IService, thus giving the new controller a lighter dependency.

I could argue that your services look very much with a repository. Look closely to the interface:

IQueryable<Photo> GetAll { get; }
Photo GetById(int photoId);
Guid AddPhoto(Guid profileId);

Looks very much like a repository to me. Maybe because the example is rather simple but I see the point of having a service if you add use case logic on it. instead of these rather simpel CRUD operations.

And you could argue that EFs DbSet and DbContext are the repositories and unit of work of the app...and at this point we enter a new zone that is somewhat out of scope of the question.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!