繁体   English   中英

将逻辑从控制器操作移动到“服务层”,而不使用ASP.NET MVC中的IoC / DI,Uo​​W和存储库模式

[英]Moving logic from controller action to a “service layer” without using IoC/DI, UoW and repository patterns in ASP.NET MVC

最近我正在研究一个ASP.NET MVC5项目,我直接进入并在动作方法中编写了我的所有逻辑,在为一些控制器执行此操作后,我注意到我一直在复制某些业务规则并且可以做被提升并在控制器之间共享。

根据我的阅读,asp.net mvc中的m是一个由实体,视图模型和服务组成的层,后者包含所有共享的业务逻辑

现在我试图让事情变得尽可能简单,我不想在某些UoW / Repo中包装实体框架并按原样使用它,我不太可能在这个应用程序生命周期中停止使用实体框架我不是在进行单元测试而且我对紧耦合不感到困扰,所以我觉得我不需要IoC容器,但我读过的所有教程似乎都使用了IoC容器或包装dbcontext /如果在UoW / Repo中。

我已经读过每个httprequest应该只有一个实例(在我看过的教程中通过IoC容器管理)的DbContext,这是通过在控制器构造函数中实例化它然后将该引用传递给控制器中需要的任何服务,然后在请求结束时处理它? 这是管理dbcontext的正确方法吗?

控制器示例:

public class SupplierController : Controller
{
    private Meerkat3Context context;
    private SupplierService supplierService;
    private ratingService SupplierRatingService;

    public SupplierController()
    {
        // instantiate the dbcontext
        this.context = new Meerkat3Context();

        // pass dbcontext into the constructors of my services
        this.supplierService = New SupplierService(context);
        this.ratingService = New SupplierRatingService(context);
    }
    public ActionResult Index(Guid id)
    {
        var supplier = supplierService.getSupplier(id);
        // construct viewmodel
        return new SupplierIndexViewModel()
        {
            SupplierId = supplier.Id,
            SupplierName = supplier.Name,

            SupplierRating = ratingService.getHighestRating(supplier.Id),
            NearbySuppliers = supplierService.getNearbySuppliers(supplier.Id),
            // etc
        };
    }
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            context.Dispose();
        }
        base.Dispose(disposing);
    }
}

服务示例:

public class SupplierService
{
    private Meerkat3Context context;

    public SupplierService(Meerkat3Context context)
    {
        this.context = context;
    }
    public Supplier getSupplier(Guid id)
    {
        return context.Where(x => x.SupplierId == id)
                      .FirstOrDefault()
                      .Select(x => new Supplier()
                      {
                           Id = x.Id,
                           Name = x.Name
                           // etc
                      });
    }
    public Supplier getNearbySuppliers(Guid id)
    {
        return context.Suppliers.Where(x => context.SupplierAddresses
                                .Where(y => y.AddressTypeId == AddressTypes.Location)
                                .Select(z => z.Address.TownCity)
                                .Contains(x.SupplierAddresses
                                           .Where(y => y.AddressTypeId == AddressTypes.Location)
                                           .FirstOrDefault()
                                           .Address.TownCity)
                                 );
    }
}

public class SupplierRatingService
{
    private Meerkat3Context context;

    public RatingService(Meerkat3Context context)
    {
        this.context = context;
    }
    public SupplierRating getHighestRating(Guid id)
    {
        return context.SupplierRating
                      .Where(x => x.SupplierId == id)
                      .OrderBy(x => x.RatingValue)
                      .FirstOrDefault()
    }
}

如果您想要的只是移出可重复使用的逻辑,那么您的方法就足够了。 但请记住:

  1. 它是不可测试的(你无法隔离你的依赖关系和

  2. 您仍在复制逻辑,即使它只是一个对象构造逻辑(例如,在您需要SupplierService每个控制器中,您也必须实例化Meerkat3Context )。 这可能会变得相当繁琐(这就是DI派上用场的地方)

如果你试图删除重复的代码,这应该是相当简单的。 在VS中,您可以突出显示一段代码并使用热键Ctrl+R,Ctrl+M进行重构,或者您可以使用上下文菜单highlight code section > right-click > Refactor > Extract Method

如果可以为所有实体复制重复代码的使用,则可以创建包含此常用功能的静态类。

public sealed class Utlities 
{
    public static CommonA() { }
    public static CommonB() { }
    ... etc...
}

您可以使用Utilities.CommonA()轻松调用它们。 减少冗余的另一种方法是使用ViewModels。 基本上创建要用作ViewModel的实体的副本,其中包含View所需的其他属性。 如果模型具有共同的数据,则创建一个基类来继承这些共性。

public class BaseViewModel 
{
    public Type Prop {get; set;}
    public Type Prop2 {get; set;}
    ...etc...
}

public class SpecificViewModel : BaseViewModel
{
    SpecificViewModel(Type Prop, Type Prop2) : base(Prop, Prop2, ...etc...) { }
    public Type specificProp {get; set;}
    ...etc...
}

如果我理解你的问题是正确的。

使用IoC容器,您的控制器看起来像。

 public class SupplierController : Controller
    {
        //the controller doesn't need to create the db context now
        //this concern is handled now by the IoC container

        private SupplierService supplierService;
        private RatingService SupplierRatingService;

        public SupplierController(SupplierService supplierService, RatingService ratingService)
        {
            // we don't have to pass the db context now to services, since we retrieve the services from the IoC container. The IoC container auto-wires the services 
            this.supplierService = supplierService;
            this.ratingService = ratingService;
        }
        public ActionResult Index(Guid id)
        {
            var supplier = supplierService.getSupplier(id);
            // construct viewmodel
            return new SupplierIndexViewModel()
            {
                SupplierId = supplier.Id,
                SupplierName = supplier.Name,

                SupplierRating = ratingService.getHighestRating(supplier.Id),
                NearbySuppliers = supplierService.getNearbySuppliers(supplier.Id),
                // etc
            };
        }
        // the controller doesn't need a dispose method since the IoC container will dispose the dbcontext for us
    }

您不必遵循依赖性倒置原则来使用IoC容器,但您可以依靠IoC容器来创建和管理服务对象的生命周期。

您配置IoC容器以根据Web请求创建dbcontext的单个实例。 好的部分是可配置的,如果您稍后决定最好为每个服务使用不同的dbcontext实例,那么您只需在一个地方而不是在每个控制器和使用new关键字的每个操作方法中更改它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM