我们有ASP MVC web项目。在阅读了大量关于正确架构的文章和讨论后,我们决定采用以下方法,尽管不仅有一种正确的做法,这是我们已经决定的方式,但我们仍然有一些疑问。ASP MVC EF6架构
我们在这里发布这不仅是为了帮助,而且要显示我们已经做了什么,以防万一它有助于某人。
我们正在开发ASP .NET MVC项目,首先使用MS SQL Server进行EF6代码。 我们已经将项目划分为3个主要层,我们将其划分为3个项目:模型,服务和网络。
- 该模型创建实体并为数据库设置DataContext。
- 该服务对数据库进行查询并将这些实体转换为DTO以将它们传递给Web层,因此Web层不知道有关数据库的任何信息。
- Web使用AutoFac作为DI(依赖注入)来调用服务层中的服务,并获取DTO以将这些DTO转换为模型视图以在视图中使用它们。
阅读大量的文章后,我们决定不执行工作的仓库模式和单位,因为在总之,我们已经看了EF作为自身工作的单位。所以我们在这里简化一些事情。 https://cockneycoder.wordpress.com/2013/04/07/why-entity-framework-renders-the-repository-pattern-obsolete/
这是我们项目的总结。现在我要通过每个项目来展示代码。我们将只显示几个实体,但我们的项目有超过100个不同的实体。
模型
数据上下文
public interface IMyContext
{
IDbSet<Language> Links { get; set; }
IDbSet<Resources> News { get; set; }
...
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
}
public class MyDataContext : DbContext, IMyContext
{
public MyDataContext() : base("connectionStringName")
{
}
public IDbSet<Language> Links { get; set; }
public IDbSet<Resources> News { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}
}
下面是我们如何声明实体
public class Link
{
public int Id{ get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
服务
这些都是通用类我们用于所有服务。如您所见,我们使用DTO从Web图层获取数据。此外,我们连接到使用Dbset = Context.Set()数据库
public interface IService
{
}
public interface IEntityService<TDto> : IService where TDto : class
{
IEnumerable<TDto> GetAll();
void Create(TDto entity);
void Update(TDto entity);
void Delete(TDto entity);
void Add(TDto entity);
void Entry(TDto existingEntity, object updatedEntity);
void Save();
}
public abstract class EntityService<T, TDto> : IEntityService<TDto> where T : class where TDto : class
{
protected IClientContext Context;
protected IDbSet<T> Dbset;
protected EntityService(IClientContext context) { Context = context; Dbset = Context.Set<T>(); }
public virtual IEnumerable<TDto> GetAll()
{
return Mapper.Map<IEnumerable<TDto>>(Dbset.AsEnumerable());
}
public virtual void Create(TDto entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
Dbset.Add(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Update(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
public virtual void Delete(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Dbset.Remove(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Add(TDto entity)
{
Dbset.Add(Mapper.Map<T>(entity));
}
public virtual void Entry(TDto existingEntity, object updatedEntity)
{
Context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
}
public virtual void Save()
{
Context.SaveChanges();
}
}
我们在这个项目中声明的DTO(这是一个很简单的例子,所以我们不必在这里把所有的代码):
public class LinkDto
{
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
那么,我们的服务之一:
public interface ILinkService : IEntityService<LinkDto>
{
IPagedList<LinkDto> GetAllLinks(string searchTitle = "", bool searchActive = false, int pageNumber = 1, int pageSize = 10);
LinkDto FindById(int id);
LinkDto Test();
}
public class LinkService : EntityService<Link, LinkDto>, ILinkService
{
public LinkService(IClientContext context) : base(context) { Dbset = context.Set<Link>(); }
public virtual IPagedList<LinkDto> GetAllLinks(bool searchActive = false, int pageNumber = 1, int pageSize = 10)
{
var links = Dbset.Where(p => p.Active).ToPagedList(pageNumber, pageSize);
return links.ToMappedPagedList<Link, LinkDto>();
}
public virtual LinkDto FindById(int id)
{
var link = Dbset.FirstOrDefault(p => p.Id == id);
return Mapper.Map<LinkDto>(link);
}
public LinkDto Test()
{
var list = (from l in Context.Links
from o in Context.Other.Where(p => p.LinkId == l.Id)
select new OtherDto
{ l.Id, l.Title, l.Url, o.Other1... }).ToList();
return list;
}
}
正如你看到的,我们使用AutoMapper(这已经改变了一点版本5)进行改造,从实体到DTO的数据。 我们的疑惑之一是如果使用“Dbset.Find”或“Dbset.FirstOrDefault”是正确的,并且如果使用“Context.Links”(用于任何实体)。
WEB
最后,我们收到的DTO和改变这些的DTO到ModelViews在我们的视图来显示Web项目。
我们需要在Global.asax Application_Start中调用AutoFac来执行DI,以便我们可以使用我们的服务。
protected void Application_Start()
{
...
Dependencies.RegisterDependencies();
AutoMapperBootstrapper.Configuration();
...
}
public class Dependencies
{
public static void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EfModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class ServiceModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.Load("MyProject.Service")).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();
}
}
public class EfModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType(typeof(MyDataContext)).As(typeof(IMyContext)).InstancePerLifetimeScope();
}
}
正如你所看到的,我们也调用AutoMapper来配置不同的地图。
然后在我们的控制器中,我们有这个。
public class LinksController : Controller
{
private readonly ILinkService _linkService;
public LinksController(ILinkService linkService)
{
_linkService = linkService;
}
public ActionResult Index()
{
var links = _linkService.GetAllLinks();
return View(links.ToMappedPagedList<LinkDto, LinksListModelAdmin>());
}
...
public ActionResult Create(LinksEditModelAdmin insertedModel)
{
try
{
if (!ModelState.IsValid) return View("Create", insertedModel);
var insertedEntity = Mapper.Map<LinkDto>(insertedModel);
_linkService.Create(insertedEntity);
return RedirectToAction("Index");
}
catch (Exception ex)
{
throw ex;
}
}
}
好了,这是......我希望这可以成为有用的人......而且我希望我们能有我们的问题一点帮助。
1)虽然我们将数据库从Web项目中分离出来,但我们确实需要Web项目中的引用来初始化数据库并注入依赖关系,这是否正确?
2)是否正确,我们已经完成了我们的实体 - > DTOs-> ViewModels?这是一个更多的工作,但我们有一切分开。
3)在Service项目中,当我们需要引用与我们在服务中使用的主实体不同的实体时,调用Context.Entity是否正确? 例如,如果我们还需要从链接服务中的新闻实体中检索数据,调用“Context.News.Where ...”是否正确?
4)我们对Automapper和EF proxy有一些问题,因为当我们调用“Dbset”来检索数据时,它会得到一个“动态代理”对象,所以Automapper无法找到合适的地图,所以按顺序为了工作,我们必须在DataContext定义中设置ProxyCreationEnabled = false。这样我们可以得到一个实体,以便将其映射到DTO。这会禁用我们不介意的LazyLoading,但这是一种正确的方法,还是有更好的方法来解决这个问题?
在此先感谢您的意见。
一个建议,多年来我发现所有这些围绕项目参考和图层的规则,或多或少是浪费时间。现在,我把精力集中在通过切片而不是分层来推动新的发展。结果相当简单的架构,imo https://vimeo.com/131633177 –