c# - MVC 3 - 如何实现服务层,我需要存储库吗?

标签 c# entity-framework asp.net-mvc-3 design-patterns ninject

我目前正在构建我的第一个 MVC 3 应用程序,使用 EF Code First、SQL CE 和 Ninject。 我已经阅读了很多关于使用存储库、工作单元和服务层的文章。我想我已经整理好了基础知识,并且已经实现了自己的实现。

这是我当前的设置:

实体

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

站点上下文

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

接口(interface):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>();
    }
}

光控器

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

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

我的问题是:

  1. 我的实现中是否存在任何我遗漏的明显缺陷?
  2. 我可以将它与 TDD 一起使用吗?通常我会看到对存储库的模拟,但我没有在上面使用过它,这会导致问题吗?
  3. 我使用 DI (Ninject) 是否正确且足够?

我是一名业余程序员,欢迎对我的代码提出任何意见和/或建议!

最佳答案

您已经了解了总体思路,但需要一段时间才能真正习惯依赖注入(inject)。我看到了一些可能的改进:

  1. 你的 IServices接口(interface)似乎是不必要的。我更愿意让 Controller 通过其构造函数指定它需要哪些服务(IPhotoService 等),而不是使用 IServices类似于某种强类型服务定位器的接口(interface)。
  2. 我看到 DateTime.Now 了吗?在那里?您将如何验证单元测试中的日期设置是否正确?如果您以后决定支持多个时区怎么办?如何使用注入(inject)日期服务来生成 CreatedDate
  3. 有一个非常好的 Ninject 扩展,专门用于 MVC。它负责插入 MVC 3 支持注入(inject)的各个点。它实现了诸如 NinjectControllerFactory 之类的东西。您所要做的就是制作您的 Global类扩展特定的基于 Ninject 的应用程序。
  4. 我建议使用 NinjectModules 来设置绑定(bind),而不是在 ControllerFactory 中设置它们。
  5. 考虑使用按约定绑定(bind),这样您就不必将每个服务显式绑定(bind)到其实现。

更新

可以找到 Ninject MVC 扩展 here .有关如何扩展 NinjectHttpApplication 的示例,请参阅 README 部分.此示例使用模块,您可以阅读有关 here 的更多信息. (它们基本上只是放置绑定(bind)代码的地方,这样您就不会违反单一职责原则。)

关于基于约定的绑定(bind),一般的想法是让您的绑定(bind)代码扫描适当的程序集并自动绑定(bind)像 IPhotoService 这样的东西。至 PhotoService基于命名约定。还有一个分机here帮助处理这些事情。有了它,您可以将这样的代码放入您的模块中:

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

上面的代码将自动绑定(bind)给定程序集中的每个类到它实现的遵循“默认”约定(例如 Bind<IPhotoService>().To<PhotoService>())的任何接口(interface)。

更新2

关于对整个请求使用相同的 DbContext,您可以这样做(使用 MVC 扩展所需的 Ninject.Web.Common 库):

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

然后,Ninject 创建的任何上下文相关服务将在请求中共享同一个实例。请注意,我个人使用过较短生命周期的上下文,所以我不知道你会如何强制在请求结束时处理上下文,但我相信它不会太难了。

关于c# - MVC 3 - 如何实现服务层,我需要存储库吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7408973/

相关文章:

asp.net - 未为 ASP.Net MVC 3 DropDownListFor 设置所选值

c# - 如何在 ASP.NET 4 WebForm 应用程序中使用 AppDomains?

c# - CheckedListBox 控件中的 WordWrap

c# - XML序列化与反序列化性能对比

c# - Windows Embedded 中的远程调试

c# - LINQ - 不返回与 SQL Server 相同的值

c# - 将整个 bool[] 设置为 false

c# - 两个 LINQ 语句对应一个可等待的 LINQ 数据库查询(优化)

c# - 在 C# 中使用泛型进行条件比较

asp.net-mvc-3 - 分析 EF Database First 时仍然存在问题