3

Juile Lerman的多元化课程“EF in Enterprise”让我印象深刻,并决定构建我的演示应用程序。在应用SOLID原则时需要帮助

我正在使用VS 2012和最新版本的EF,SQL Server和MVC。我正在构建一个应用SOLID原则的演示应用程序。我正在这样做,以更好地了解如何执行单元测试。

我对这个演示应用程序使用了DB第一种方法。它只包含一个名为UserDetails的表,下面是它在SQL服务器中的外观。我将使用此表进行CRUD操作。 enter image description here

下面是如何我已经分层我的应用程序:

1 WESModel解决方案:这一层包含了我Model1.edmx文件,如下上下文类。

namespace WESModel 
{ 
    using System; 
    using System.Data.Entity; 
    using System.Data.Entity.Infrastructure; 
    using WESDomain; 

    public partial class WESMVCEntities : DbContext 
    { 
     public WESMVCEntities() 
      : base("name=WESMVCEntities") 
     { 
     } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      throw new UnintentionalCodeFirstException(); 
     } 

     public DbSet<UserDetail> UserDetails { get; set; } 
    } 
} 

2. WESDomain解决方案:此层包含我的域类(或POCO类)。这些POCO类实际上是在我的WESModel图层中自动生成的。我将它们移到了这一层。以下是单个POCO类的外观。

namespace WESDomain 
{ 
    using System; 
    using System.Collections.Generic; 

    public partial class UserDetail:IUserDetail 
    { 
     public int Id { get; set; } 
     public string UserName { get; set; } 
    } 
} 

3:WESDataLayer解决方案:此层包含从我的上述2层参照的DLL。 此图层具有我的Repository类,如下所示。现在,我保持IRepository在同级别:)

namespace WESDataLayer 
{ 
    public class UserDetailRepository : IUserDetailRepository 
    { 
     WESMVCEntities context = new WESMVCEntities(); 

     public IQueryable<IUserDetail> All 
     { 
      get { return context.UserDetails; } 
     } 

     public IQueryable<IUserDetail> AllIncluding(params Expression<Func<IUserDetail, object>>[] includeProperties) 
     { 
      IQueryable<IUserDetail> query = context.UserDetails; 
      foreach (var includeProperty in includeProperties) { 
       query = query.Include(includeProperty); 
      } 
      return query; 
     } 

     public IUserDetail Find(int id) 
     { 
      return context.UserDetails.Find(id); 
     } 

     public void InsertOrUpdate(UserDetail userdetail) 
     { 
      if (userdetail.Id == default(int)) { 
       // New entity 
       context.UserDetails.Add(userdetail); 
      } else { 
       // Existing entity 
       context.Entry(userdetail).State = EntityState.Modified; 
      } 
     } 

     public void Delete(int id) 
     { 
      var userdetail = context.UserDetails.Find(id); 
      context.UserDetails.Remove(userdetail); 
     } 

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

     public void Dispose() 
     { 
      context.Dispose(); 
     } 
    } 

    public interface IUserDetailRepository : IDisposable 
    { 
     IQueryable<IUserDetail> All { get; } 
     IQueryable<IUserDetail> AllIncluding(params Expression<Func<UserDetail, object>>[] includeProperties); 
     UserDetail Find(int id); 
     void InsertOrUpdate(UserDetail userdetail); 
     void Delete(int id); 
     void Save(); 
    } 
} 

4:ConsoleApplication1解决方案:这是我的UI层。这将是我的最终应用程序中的MVC应用程序。在这里,我简单地查询数据库并显示数据。这是代码的外观。

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IUserDetailRepository repo = new UserDetailRepository(); 

      var count = repo.All.ToList().Count().ToString(); 
      Console.WriteLine("Count: {0}", count); 
      Console.ReadLine(); 

     } 
    } 
} 

问题:我的UI层没有任何裁判EF DLL。但是,它有一个Repository类的实例。在MVC应用程序中,我的控制器将拥有存储库类或UnitOfWork的实例。

a)这是正确的做法吗?

b)有什么办法可以抽象吗?

c)如果将来我想用Dapper或任何其他ORM工具替换EF会怎么样?

d)如何使我的DI工具适合这个项目?它应该在哪一层? e)单元测试。我知道StructureMap,并希望在这个项目中使用它,以便将来我可以将它与Ninject交换。我如何实现这一目标?

感谢您阅读这个大问题,我真的很感激,如果任何人都可以指向正确的方向。

+0

'IRepository'不应该返回'IQueryable'类型。请,[看到这个链接](https://stackoverflow.com/questions/33755499/entity-framework-repository-pattern-why-not-return-iqueryable) – StepUp

回答

3

问:我的UI层没有任何裁判EF DLL。但是,它有 Repository类的一个实例。在MVC应用程序中,我的控制器 将具有存储库类或UnitOfWork的实例。

是的,UI图层类不能有任何对EF的引用。但要做到这一点,他们不能引用具体的存储库。在MVC应用程序中,如果你不使用服务层,Controller将在IUserDetailRepository上有一个引用,并等待构造的具体类型。 关于UnitOfWork,它取决于您的实施:-)

a)这是正确的做法吗?

做正确的事情叫做“松耦合”,看起来你的设计正在选择这种方式。

b)有什么办法可以抽象吗?

是的,你可以使用Dependency Resolver。这样,不需要参考具体的类型,你将只有一个基于抽象的代码

c)如果将来我想用EF和Dapper或其他ORM工具换出EF呢?

您必须拥有数据访问层,例如包含IXxxRepository合约具体实现的库。在你的情况下,它将是EF实现。当你改变Dapper时,你将不得不重新实现这个层。重构具有可接受的限制。

d)如何使我的DI工具适合此项目?它应该在哪一层?

放置您的DI工具的最佳位置将是UI层。在应用程序启动时,您将配置依赖关系绑定,并且所有内容都将自动生效;)

e)单元测试。我知道StructureMap,并希望在这个项目中使用它,以便将来我可以将它与Ninject交换。我如何实现这一目标?

你想拔掉你的Dependency Resolver来插入另一个?没问题,只有在编写DR的配置时才有一个预测,以便与应用程序保持最小的耦合。在某些情况下,有一些技巧来限制耦合......在我目前正在开发的项目中,我们首先有一个MVC应用程序和一个服务层,业务层,数据访问层和基础结构层。我们使用Ninject作为灾难恢复,而基础架构和Web UI层是唯一有Ninject参考的。拔出它非常容易,我们已经以这种方式尝试了Unity。

还有一件事,你不应该有一个UserDetail的合同。没有必要,在无状态类上使用依赖注入,而不是像DTO这样的所有类。

+0

哇..很好的解释。我现在需要更多的信息1.任何指向使用依赖关系解析器的链接? 2.最后你说“不应该为UserDetail订立合约”。你指的是哪一层?感谢您的时间和详细的答复。 – NoobDeveloper

+0

我指的是你的类UserDetail,它的合同IUserDetail,在你的域层。对我来说,创建Business Object的契约已经过去了(或许你有几个实现)。 – Julien

+0

你想要一般链接使用依赖关系解析器?我只是有使用Ninject的链接:)但是从示例中,您将了解使用DR的整体架构... 试试这个http://stefanoricciardi.com/2011/01/21/ninject-mini-tutorial-part -1/ – Julien

0

简述:

模型对IRepository依赖(IRepository的实现可以是任何东西,小巧玲珑,EF,ADO.Net等)留存数据,执行查询,等你的模型具有商业规则。

你的视图(控制台,WPF,网页)具有层上依赖于视图和模型之间,无论是presenter(MVP),controller(MVC)或viewmodel(MVVM)。

该中间层与模型一起使用来保存数据。

您可以使用依赖点来使用DI。

单元测试可应用于任何层,但请确保您专门覆盖业务规则。

IUserDetailRepository看起来像一个存储库,你的模型应该使用它。通过这种方式,您可以将数据库实现分离为接口抽象,如前所述,真正的实现可以是任何类似EF,Dapper等。

在MVC模型中,控制器调用模型以应用业务规则并坚持数据。

+0

我需要更多的澄清。你说我的模型依赖于IRepository。您是否在上面的代码中引用WESDataLayer解决方案项目? 2.如果是MVC应用程序,我的控制器应该如何查询数据?我应该在Controller和WESDataLayer解决方案之间的上面的代码中有另一个层? 其实我很困惑你上面说的这个“模型”究竟是什么。从我的应用角度来看,这里的“模型”是什么?谢谢你的时间。 – NoobDeveloper

2

如果使用隐式变量键入而不是显式变量键入(即删除var关键字),则可以更轻松地确定依赖关系。尽可能使用接口(IUserDetailRepository),而不是使用类(UserDetailRepository)。

例子:

1)允许编译器以确定类型

var repo = new UserDetailRepository();

2)类型由类引用

UserDetailRepository repo = new UserDetailRepository();

3)确定的类型通过接口

确定

IUserDetailRepository repo = new UserDetailRepository();

通过允许类型由接口而不是由编译器确定,可以交换符合相同接口的不同引用。 IUserDetailRepository repo = new DapperUserDetailRepository();

而且,你是在一个叫做控制反转(IOC)的原则,这是使用特定的IoC容器(NinjectCastleWinsorUnity等)来自动解决您的依赖关系,因此您的实践的边界请勿直接致电new关键字。

既然你提到StructureMap,下面是如何工作的一个例子:

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IContainer container = ConfigureDependencies(); 
      IUserDetailRepository repo = container.GetInstance<IUserDetailRepository>(); 

      var count = repo.All.ToList().Count().ToString(); 
      Console.WriteLine("Count: {0}", count); 
      Console.ReadLine(); 

     } 

     private static IContainer ConfigureDependencies() { 
      return new Container(x =>{ 
       x.For<IUserDetailRepository>().Use<UserDetailRepository>(); 
      }); 
     } 
    } 
} 
+0

其实,我已经完成了与你在答案中提到的完全相同的内容。我已经更新了原始文章以反映相同内容。我有几个问题。我知道StructureMap,并希望在这个项目中使用它,以便将来我可以将它与Ninject交换。我如何实现这一目标? – NoobDeveloper

+0

再次感谢安德鲁。我很抱歉混淆。我应该更清楚。我知道如何使用StructureMap。你的示例代码是我可以从UI层/ MVC应用程序的控制器中使用它的最简单的方法。不过,我想知道如何将它抽象出来/松散地将它与UI/Controller耦合起来,以便我可以将它与Ninject交换出去,如果我需要的话。 – NoobDeveloper

+0

我不是一个完整的DI容器专家,但根据我的经验,DI容器是与应用程序紧密结合的一部分,为了使它有效。尝试提取DI容器最可能比实际实现中的差异更值得考虑。 – Claies