我们有ASP MVC网络项目。在阅读了关于正确架构的stackoverflow中的大量文章和讨论之后,我们决定采用以下方法,尽管不仅有一种正确的做法,这就是我们决定的方式,但我们仍有一些疑问
我们在这里发布这个不仅是为了帮助,而且是为了展示我们所做的事情,以防它对某人有所帮助。
我们正在使用ASP .NET MVC项目,EF6 Code首先使用MS SQL Server。 我们将项目划分为3个主要层,我们将这些层分成3个项目:模型,服务和Web。
在阅读了很多文章之后,我们决定不实施存储库模式和工作单元,因为总的来说,我们已经将EF作为一个工作单元本身。所以我们在这里简化一些事情。 https://cockneycoder.wordpress.com/2013/04/07/why-entity-framework-renders-the-repository-pattern-obsolete/
这是我们项目的总结。现在,我将浏览每个项目以显示代码。我们将只显示几个实体,但我们的项目有100多个不同的实体。
MODEL
数据上下文
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数据。 我们怀疑的一个问题是如果使用&#34; Dbset.Find&#34;或&#34; Dbset.FirstOrDefault&#34;是正确的,如果使用&#34; Context.Links&#34; (对于任何实体)。
网络
最后我们接收DTO的Web项目,并将这些DTO转换为ModelViews以显示在我们的视图中。
我们需要在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)我们采用实体 - &gt; DTOs-&gt; ViewModels的方法是否正确?它的工作要多一些,但我们把所有东西分开了。
3)在Service项目中,当我们需要引用一个不同于我们在服务中使用的主实体的实体时,调用Context.Entity是否正确? 例如,如果我们需要从链接服务中的新闻实体中检索数据,那么调用&#34; Context.News.Where是否正确...&#34;?
4)我们确实在Automapper和EF代理方面遇到了一些问题,因为当我们调用&#34; Dbset&#34;为了检索数据,它获得了一个动态代理&#34;因此,Automapper无法找到合适的映射,因此,为了工作,我们必须在DataContext定义中设置ProxyCreationEnabled = false。这样我们就可以获得一个实体,以便将它映射到DTO。这会禁用LazyLoading,我们不介意,但这是一种正确的方法还是有更好的方法来解决这个问题?
提前感谢您的意见。
答案 0 :(得分:1)
问题编号。 2
实体 - &GT; DTOs-&GT;的ViewModels?是好方法 因为你正在进行清洁分离,所以程序员可以轻松地协同工作。 设计ViewModel,Views和Controllers的人不必担心服务层或DTO实现,因为他将在其他开发人员完成实现时进行映射。
问题编号。 4
当标志ProxyCreationEnabled设置为false时,将不会使用创建实体的新实例来创建代理实例。这可能不是问题,但我们可以使用DbSet的Create方法创建代理实例。
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create();
}
Create方法有一个重载版本,它接受泛型类型。这可用于创建派生类型的实例。
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create<Student>();
}
如果实体的代理类型没有值(它与代理无关),Create方法只会创建实体类型的实例。 Create方法不会使用上下文对象添加或附加实体。
另外我读了一些内容,如果设置ProxyCreationEnabled = false
,除非在父对象上调用Include
方法,否则不会为某些父对象加载子元素。