需要帮助应用SOLID原则 [英] Need Help in applying SOLID principles

查看:171
本文介绍了需要帮助应用SOLID原则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我在使用VS 2012和最新版本的EF,真的让Juile Lerman对EF in Enterprise的复数课程印象深刻, SQL Server和MVC。我正在构建一个应用SOLID原理的演示应用程序。我正在这样做,以更好地了解如何实施DI&单元测试。



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



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



1。 WESModel解决方案:此图层包含我的Model1.edmx文件和上下文类如下。

 命名空间WESModel 
{
using System;
使用System.Data.Entity;
使用System.Data.Entity.Infrastructure;
使用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;组;
}
}

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

 命名空间WESDomain 
{
using System;
使用System.Collections.Generic;

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

3:WESDataLayer解决方案: strong>这个图层包含了我上面2层的dll的引用。
这个图层有我的Repository类,如下所示。现在,我将IRepository保存在同一个类中:)

 命名空间WESDataLayer 
{
public UserDetailRepository类:IUserDetailRepository
{
WESMVCEntities context = new WESMVCEntities();

public IQueryable< IUserDetail>全部
{
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);
}
返回查询;
}

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

public void InsertOrUpdate(UserDetail userdetail)
{
if(userdetail.Id == default(int)){
//新实体
context.UserDetails.Add(userdetail);
} else {
//现有实体
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>全部{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应用程序。这里我只是查询数据库并显示数据。这是代码如何。

 命名空间ConsoleApplication1 
{
类程序
{
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进行交换。我该如何实现?感谢您阅读这个大问题,如果有任何人可以指出正确的方向,我真的很感激。

解决方案


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


是的,UI层类不能有任何参考EF。但是要做到这一点,他们不能参考具体的资料库。在MVC应用程序中,如果不使用服务层,控制器将在IUserDetailRepository上具有引用,并从构造中等待具体类型。
关于UnitOfWork,这取决于你的实现: - )


a)这是正确的吗? >

正确的做法是称为松耦合,似乎您的设计正在选择这种方式。


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


是的,您可以使用依赖解决方案。这样,不需要引用具体的类型,你将只有一个代码只能基于抽象。


c)如果将来我想要使用Dapper或任何其他ORM工具交换EF?


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


d)我如何适应我的DI工具在这个项目?应该在哪一层?


放置DI工具的最佳位置将是UI层。在应用程序启动时,您将配置依赖项绑定,一切都将自动工作;)


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


你想拔掉你的Dependency Resolver来插上另一个?没有问题,只要您的DR的编码配置与您的应用程序的最小耦合才能预测。在某些情况下,有一些限制耦合的提示...在我目前正在开展的项目中,我们首先使用MVC应用程序和服务层,业务层,数据访问层和基础设施层。我们使用Ninject作为DR,而结构和Web UI层是唯一可以在Ninject上引用的。很容易拔掉它,我们已经以这种方式尝试了Unity。



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


Really impressed with Juile Lerman's pluralsight course on "EF in Enterprise" and decided to build my demo app.

I am using VS 2012 and latest versions of EF,SQL Server and MVC. I am building a demo application which applies SOLID principles. I am doing this to better understand how to implement DI & unit testing.

I have used DB first approach for this demo application. It contains only one table named UserDetails and below is how it looks in SQL server. I will use this table for CRUD operations.

Below is how i have layered my application :

1. WESModel Solution: This layer contains my Model1.edmx file and the context class as below.

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 Solution: This layer contains my Domain classes (or POCO classes). These POCO classes were actually auto generated in my WESModel layer. I moved them out to this layer. Here is how the single POCO class looks.

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 Solution: This layer contains reference to dlls from my above 2 layers. This layer has my Repository class as shown below. For now , I am keeping the IRepository in the same class :)

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 Solution: This is my UI layer. It will be my MVC application in my final app. Here i simply query the DB and display the data. This is how the code looks.

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

        }
    }
}

Question: My UI layer does not have any ref to EF DLL. However, It has an instance of Repository class. In MVC application, my controller will have an instance of repository class or UnitOfWork.

a) Is this the right thing to do ?

b) Is there any way i can abstract it ?

c) What if in future i want to swap out EF with Dapper or any other ORM tool ?

d) How do i fit my DI tool in this project ? In which layer it should be ?

e) Unit testing. I am aware of StructureMap and want to make use of it in this project in such a way that in future i should be able to swap it out with Ninject. How do i achieve this ?

Thanks for reading this big question and i really appreciate if anyone can point me in right direction.

解决方案

Question: My UI layer does not have any ref to EF DLL. However, It has an instance of Repository class. In MVC application, my controller will have an instance of repository class or UnitOfWork.

Yes, UI layer classes must not have any reference to EF. But to do this, they can't have a reference to the concrete repository. In MVC Application, if you don't use a Service Layer, the Controller will have a reference just on IUserDetailRepository, and wait a concrete type from construction. About UnitOfWork, it depends on your implementation :-)

a) Is this the right thing to do ?

The right thing to do is called "loose coupling", it seems that your design is choosing this way.

b) Is there any way i can abstract it ?

Yes, you can use a Dependency Resolver. This way, no need to reference concrete types, you will have a code only based on abstractions

c) What if in future i want to swap out EF with Dapper or any other ORM tool ?

You must have a Data Access Layer, for example, a library containing your concrete implementations of your IXxxRepository contracts. In your case, it will be EF implementations. When you will change for Dapper, you will have to re-implement this layer. The refactoring has an acceptable limit.

d) How do i fit my DI tool in this project ? In which layer it should be ?

Best place to place your DI tool will be the UI layer. On application start, you will configure dependencies bindings and everything will work automagically ;)

e) Unit testing. I am aware of StructureMap and want to make use of it in this project in such a way that in future i should be able to swap it out with Ninject. How do i achieve this ?

You want to unplug your Dependency Resolver to plug an other? No problem, just have a forecast when coding configuration of your DR to have the minimum coupling with your application. There are some tips to limit coupling in some cases... In the project I am currently working on, we have first an MVC application and a Service Layer, Business Layer, Data Access Layer, and Infrastructure Layer. We use Ninject as DR, and the infratructure and Web UI layers are the only that have a reference on Ninject. It's very easy to unplug it, and we already tried Unity this way.

One more thing, you shouldn't have a contract for UserDetail. There is no need for that, use Dependency Injection on stateless classes rather than on all classes like DTOs.

这篇关于需要帮助应用SOLID原则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆