我目前正在构建我的第一个 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);
}
}
我可以构建我的解决方案,它似乎工作正常。
我的问题是:
- 我的实现中是否存在任何我遗漏的明显缺陷?
- 我可以将它与 TDD 一起使用吗?通常我会看到对存储库的模拟,但我没有在上面使用过它,这会导致问题吗?
- 我使用 DI (Ninject) 是否正确且足够?
我是一名业余程序员,欢迎对我的代码提出任何意见和/或建议!
最佳答案
您已经了解了总体思路,但需要一段时间才能真正习惯依赖注入(inject)。我看到了一些可能的改进:
- 你的
IServices
接口(interface)似乎是不必要的。我更愿意让 Controller 通过其构造函数指定它需要哪些服务(IPhotoService 等),而不是使用IServices
类似于某种强类型服务定位器的接口(interface)。 - 我看到
DateTime.Now
了吗?在那里?您将如何验证单元测试中的日期设置是否正确?如果您以后决定支持多个时区怎么办?如何使用注入(inject)日期服务来生成CreatedDate
? - 有一个非常好的 Ninject 扩展,专门用于 MVC。它负责插入 MVC 3 支持注入(inject)的各个点。它实现了诸如 NinjectControllerFactory 之类的东西。您所要做的就是制作您的
Global
类扩展特定的基于 Ninject 的应用程序。 - 我建议使用 NinjectModules 来设置绑定(bind),而不是在 ControllerFactory 中设置它们。
- 考虑使用按约定绑定(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/