MVC 3 - 如何实现一个服务层,我需要仓库? [英] MVC 3 - how to implement a service layer, do I need repositories?

查看:115
本文介绍了MVC 3 - 如何实现一个服务层,我需要仓库?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在建设我的第一个MVC 3应用程序,使用EF code首先,SQL CE和Ninject。
我读了很多关于使用存储库,工作和服务层的单位。我想我已经得到了基本知识整理出来,和我已经作出了自己的实现。

I am currently building my first MVC 3 application, using EF Code First, SQL CE and Ninject. I have read a lot about using Repositories, Unit of Work and Service Layers. I think I have got the basics sorted out, and I have made my own implementation.

这是我的当前设置:

实体

public class Entity
{
    public DateTime CreatedDate { get; set; }
    public Entity()
    {
        CreatedDate = DateTime.Now;
    }
}

public class Profile : Entity
{
    [Key]
    public Guid UserId { get; set; }
    public string ProfileName { get; set; }

    public virtual ICollection<Photo> Photos { get; set; }

    public Profile()
    {
        Photos = new List<Photo>();
    }

public class Photo : Entity
{
    [Key]
    public int Id { get; set; }
    public Guid FileName { get; set; }
    public string Description { get; set; }

    public virtual Profile Profile { get; set; }
    public Photo()
    {
        FileName = Guid.NewGuid();
    }
}

SiteContext

public class SiteContext : DbContext
{
    public DbSet<Profile> Profiles { get; set; }
    public DbSet<Photo> Photos { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

接口:IServices

public interface IServices : IDisposable
{
    PhotoService PhotoService { get; }
    ProfileService ProfileService { get; }

    void Save();
}

实施:服务

public class Services : IServices, IDisposable
{
    private SiteContext _context = new SiteContext();

    private PhotoService _photoService;
    private ProfileService _profileService;

    public PhotoService PhotoService
    {
        get
        {
            if (_photoService == null)
                _photoService = new PhotoService(_context);

            return _photoService;
        }
    }

    public ProfileService ProfileService
    {
        get
        {
            if (_profileService == null)
                _profileService = new ProfileService(_context);

            return _profileService;
        }
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

接口

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

实施

public class PhotoService : IPhotoService
{
    private SiteContext _siteContext;

    public PhotoService(SiteContext siteContext)
    {
        _siteContext = siteContext;
    }

    public IQueryable<Photo> GetAll
    {
        get
        {
            return _siteContext.Photos;
        }
    }

    public Photo GetById(int photoId)
    {
        return _siteContext.Photos.FirstOrDefault(p => p.Id == photoId);
    }

    public Guid AddPhoto(Guid profileId)
    {
        Photo photo = new Photo();

        Profile profile = _siteContext.Profiles.FirstOrDefault(p => p.UserId == profileId);

        photo.Profile = profile;
        _siteContext.Photos.Add(photo);

        return photo.FileName;
    }
}

的Global.asax

protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());

        Database.SetInitializer<SiteContext>(new SiteInitializer());
    }

NinjectControllerFactory

public class NinjectControllerFactory : DefaultControllerFactory
{
    private IKernel ninjectKernel;
    public NinjectControllerFactory()
    {
        ninjectKernel = new StandardKernel();
        AddBindings();
    }
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        return controllerType == null
            ? null
        : (IController)ninjectKernel.Get(controllerType);
    }

    private void AddBindings()
    {
        ninjectKernel.Bind<IServices>().To<Services>();
    }
}

PhotoController

public class PhotoController : Controller
{
    private IServices _services;

    public PhotoController(IServices services)
    {
        _services = services;
    }

    public ActionResult Show(int photoId)
    {
        Photo photo = _services.PhotoService.GetById(photoId);

        if (photo != null)
        {
            string currentProfile = "Profile1";

            _services.PhotoService.AddHit(photo, currentProfile);

            _services.Save();

            return View(photo);
        }
        else
        {
            // Add error message to layout
            TempData["message"] = "Photo not found!";
            return RedirectToAction("List");
        }
    }

    protected override void Dispose(bool disposing)
    {
        _services.Dispose();
        base.Dispose(disposing);
    }
}

我可以建立我的解决方案,它似乎正常工作。

I can build my solution and it seems to be working correctly.

我的问题是:


  1. 是否有我在执行任何明显的缺陷,我很想念?

  2. 我能与TDD使用它?通常我看到仓库的嘲笑,但我没有用在上面,将这一事业的问题?

  3. 我使用的DI(Ninject)正确,还不够吗?

我是一个爱好程序员,所以任何意见和/或建议到我的code是欢迎!

I am a hobby programmer, so any comments and/or suggestions to my code are welcome!

推荐答案

您已经得到了普遍的想法,但还需要一段时间才能真正习惯了依赖注入。我看到被提出了一些可能的改进:

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. IServices 界面似乎没有必要。我想preFER有控制器指定哪些服务需要(IPhotoService等),通过它的构造函数,而不是使用 IServices 界面像某种强烈-typed服务定位器。

  2. 难道我看到一个 DateTime.Now 在那里?你将如何验证日期获取单元测试设置是否正确?如果你决定了后来以支持多个时区?如何使用一个注入的日期服务产生 CreatedDate

  3. 有专门为MVC很好Ninject扩展。这需要堵到了MVC 3支持注入各点的照顾。它实现的东西像你NinjectControllerFactory。所有你需要做的就是让你的全球类扩展特定的基于Ninject的应用程序。

  4. 我建议使用NinjectModules设置您的绑定,而不是在你的ControllerFactory设置它们。

  5. 考虑使用具有约束力的公约,这样你就不必每个服务显式绑定到其执行情况。

  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.

在Ninject MVC扩展可以在这里找到 。参阅README部分的如何延长 NinjectHttpApplication 的例子。这个例子使用的模块,你可以阅读更多关于这里。 (他们基本上只是把你绑定code,这样你就不会违反单一职责原则的地方。)

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.)

对于基于约定绑定,一般的想法是让你的绑定code扫描适当的组件和自动绑定的东西像 IPhotoService 照片服务基础上的命名约定。还有另一种延伸这里帮助这样的事情。有了它,你可以把code这样的模块中:

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();
                });

以上code将自动绑定在指定的装配任何接口它实现后面的默认约定每一个类(如绑定&LT; IPhotoService&GT;()到&lt;照片服务&GT; ())。

关于使用相同的DbContext为整个请求,你可以做这样的事情(使用 Ninject.Web.Common 库,它是由MVC扩展所需)

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();

然后就是Ninject创建将在请求共享同一个实例的上下文相关的服务。请注意,我曾亲自使用短居住环境,所以我不知道把我的头顶部,你会如何强制上下文在请求结束时进行处理,但我敢肯定它不会是太困难了。

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.

这篇关于MVC 3 - 如何实现一个服务层,我需要仓库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆