簡體   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