利用控制器和视图中的继承 [英] taking advantage of inheritance in Controllers and Views

查看:62
本文介绍了利用控制器和视图中的继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在codereview.stackexchange.com上发布了此评论前一段时间...我觉得它可能更适合stackoverflow,因为它比代码审查更是一个问题.

I had posted this review on codereview.stackexchange.com a while ago... I feel it may be more suitable for stackoverflow, since it is more of a question than a code review.

这需要一些解释,请忍受我.

我正在ASP.NET MVC中开发一个电子商务网站.用户可以在网站上发布不同类型的广告.

I am developing an e-commerce website in ASP.NET MVC. Users can post advertisements of different types on the site.

我正在使用继承来定义广告类型,这个问题是关于利用层次结构来删除Controllers和Views中重复的代码.

I am using inheritance to define my Ad types, and this question is about taking advantage of the hierarchical structure to remove repeated code in Controllers and Views.

我有不同的广告类型:SimpleAdCarRealEstateRental.

I have different Ad types: SimpleAd, Car and RealEstateRental.

每个广告均源自具有所有常用属性的AdBase:

Every Ad is derived from AdBase which has all the common properties:

public abstract class AdBase
{
    public long AdBaseId { get; set; }
    public bool IsActive { get; set; }
    public long UserId { get; set; }
    public string Title { get; set; }
    public short AdDurationInDays { get; set; }
    public string PhotosFolder { get; set; }
}

现在其他广告都从该基类派生:

Now other Ads are derived from this base class:

public class SimpleAd : AdBase
{
    public decimal Price { get; set; }
}

public class Car : AdBase
{
    public decimal Price { get; set; }
    public string Make { get; set; }
}

public class RealEstateRental : AdBase
{
    public decimal WeeklyRent { get; set; }
    public DateTime AvailableFrom { get; set; }
    public short NoOfBedrooms { get; set; }
    public short NoOfBathrooms { get; set; }
}

我正在使用Entity Framework与数据库进行交互,并且正在使用工作单元和存储库模式:

I am using Entity Framework to interact with database and I am using Unit of Work and Repository patterns:

我有一个通用的AdBaseRepository,其中包含所有常见的广告方法:

I have a generic AdBaseRepository with all the common ad methods:

public abstract class AdBaseRepository<TEntity> where TEntity : AdBase
{
    protected readonly ApplicationDbContext Context;

    public AdBaseRepository(ApplicationDbContext context)
    {
       Context = context; 
    }

    public TEntity Get(long adBaseId)
    {
        return Context.AdBase.OfType<TEntity>()
                  .Where(r => r.IsActive == true && r.AdBaseId == adBaseId)
                  .FirstOrDefault();
    }

    // more common methods here...
}

其他广告存储库继承自上述类:

Other ad repositories inherit from the above class:

public class SimpleAdRepository : AdBaseRepository<SimpleAd>
{
    public SimpleAdRepository(ApplicationDbContext context) : base(context)
    {
    }
}

public class CarRepository : AdBaseRepository<Car>
{
    public CarRepository(ApplicationDbContext context) : base(context)
    {
    }

    // methods which apply only to car here...
}

这是我的工作单位:

public class UnitOfWork
{
    protected readonly ApplicationDbContext Context;

    public UnitOfWork(ApplicationDbContext context)
    {
        Context = context;
        SimpleAd = new SimpleAdRepository(Context);
        RealEstateRental = new RealEstateRentalRepository(Context);
        Car = new CarRepository(Context);
    }

    public SimpleAdRepository SimpleAd { get; private set; }
    public RealEstateRentalRepository RealEstateRental { get; private set; }
    public CarRepository Car { get; private set; }

    public int SaveChanges()
    {
        return Context.SaveChanges();
    }

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

到目前为止,我对所有事情都很满意...但是问题是我不知道如何在我的Controllers和Views中利用这种继承层次结构.

目前,我有3个控制器:SimpleAdControllerCarControllerRealEstateRentalController:

At the moment, I have 3 Controllers: SimpleAdController, CarController and RealEstateRentalController:

public class SimpleAdController : ControllerBase
{
    private UnitOfWork _unitOfWork;

    public SimpleAdController(UnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    [HttpGet]
    // display specific ad
    public ActionResult Display(long id)
    {
        SimpleAd simpleAd = _unitOfWork.SimpleAd.Get(id);
        /* 
         * I have not included my ViewModel Classes in this question to keep
         * it small, but the ViewModels follow the same inheritance pattern
         */
        var simpleAdDetailsViewModel = Mapper.Map<SimpleAdDetailsViewModel>(simpleAd);
        return View(simpleAdDetailsViewModel);
    }
}

CarControllerRealEstateRentalController具有相同的Display方法,除了广告的类型不同(例如,我在CarController中具有):

CarController and RealEstateRentalController have the same Display method, except the type of the Ad is different (e.g. in CarController I have):

    public ActionResult Display(long id)
    {
        Car car = _unitOfWork.Car.Get(id);
        var carViewModel = Mapper.Map<CarViewModel>(car);
        return View(car);
    }

我想要实现的是创建一个AdBaseController,将所有常用方法放入其中,如下所示:

What I wanted to achieve was to create an AdBaseController to put all the common methods in it, something like this:

public class AdBaseController : ControllerBase
{
    private UnitOfWork _unitOfWork;

    public AdBaseController(UnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    // Display for generic ad type
    [HttpGet]
    public ActionResult Display(long id)
    {
        // SimpleAd simpleAd = _unitOfWork.SimpleAd.Get(id);
        /* 
         * I need to replace the above line with a generic ad type... 
         * something like: _unitOfWork<TAd>.GenericAdRepository.Get(id)
         */

        // var simpleAdDetailsViewModel = Mapper.Map<SimpleAdDetailsViewModel>(simpleAd);
        // return View(simpleAdDetailsViewModel);
        /* 
         * similarly I have to replace the above 2 lines with a generic type
         */
    }
}

如果执行上述操作,则我的广告控制器可以从中继承而来,不需要在每个控制器中都重复相同的显示方法...但是,我需要使我的UnitOfWork通用.还是有2个UoW(通用和非通用)...我不确定这是否是个好主意? 是否有关于AdBaseController 的建议?

If I do the above, then my Ad Controllers can inherit from it and I don't need to repeat the same Display Method in every one of them... but then I need to make my UnitOfWork generic... or have 2 UoW (generic and non-generic)... which I am not sure if it is a good idea? Any recommendation on having a AdBaseController?

类似地,我在视图中重复了很多代码.例如,这是显示SimpleAdView:

Similarly I am repeating a lot of code in my Views. For example, this is the display SimpleAdView:

<div class="row">
    <div class="col-l">
        @*this partial view shows Ad photos and is common code for all ad types*@
        @Html.Partial("DisplayAd/_Photos", Model)
    </div>
    <div class="col-r">
        <div class="form-row">
            @*Common in all ads*@
            <h5>@Model.Title</h5>
        </div>

        @*showing ad specific fields here*@
        <div class="form-row">
            <h5 class="price">$@Model.Price</h5>
        </div>

        @*Ad heading is common among all ad types*@
        @Html.Partial("DisplayAd/_AdBaseHeading", Model)
    </div>
</div>
@*Ad Description is common among all ad types*@
@Html.Partial("DisplayAd/_Description", Model)

这是我的显示器CarView:

<div class="row">
    <div class="col-l">
        @*Common in all ads*@
        @Html.Partial("DisplayAd/_Photos", Model)
    </div>
    <div class="col-r">
        <div class="form-row">
            @*Common in all ads*@
            <h5>@Model.Title</h5>
        </div>

       @*Price and Make are specific to Car*@ 
        <div class="form-row">
            <h5 class="price">$@Model.Price</h5>
        </div>
        <div class="form-row">
            <h5 class="make">@Model.Make</h5>
        </div>

        @*Common in all ads*@ 
        @Html.Partial("DisplayAd/_AdBaseHeading", Model)
    </div>
</div>
@*Common in all ads*@
@Html.Partial("DisplayAd/_Description", Model)

同样,我觉得我在每个视图中都重复了很多代码.我试图通过将它们放在公共的局部视图中来减少重复代码的数量. 我不确定是否有更好的方法?

Again, I feel like I am repeating a lot of code in each view. I have tried to reduce the amount of repeated code by putting them in common Partial Views. I am not sure if there is a better way to do this?

推荐答案

从技术上讲,这是可能的.对于相似的实体,您可以引入枚举并使用它来表示您在controller中处理的实体类型.您可以创建通用视图来处理类似的广告(但是,根据模型广告类型,您当然需要显示/隐藏相应的UI元素).这是controller的伪代码来说明这个想法:

Technically it is possible. For similar entities you can introduce enumeration and use it to indicate what entity type you deal with in controller. You can create generic view to handle similar ads (but of course you will need to show/hide corresponding UI elements depending on the model ad type). this is the pseudo code for controller to illustrate the idea:

using System.Threading.Tasks;
using AutoMapper;
using MyNamespace.Data;
using Microsoft.AspNetCore.Mvc;
using MyNamespace.ViewModels;

namespace MyNamespace
{
    public enum AdType
    {
        [Description("Simple Ad")]
        SimpleAd = 0,

        [Description("Car")]
        Car = 1,

        [Description("Real Estate Rental")]
        RealEstateRental = 2
    }

    public class AdController : Controller
    {
        private readonly ApplicationDbContext _context;
        private readonly IMapper _mapper;

        public AdController(
            ApplicationDbContext context,
            IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        [HttpGet("Ad/{type}")]
        public IActionResult Index(AdType? type = AdType.SimpleAd)
        {
            switch (type)
            {
                case AdType.RealEstateRental:
                    return RedirectToAction("RealEstateRental");
                case AdType.Car:
                    return RedirectToAction("Car");
                case AdType.SimpleAd:
                default:
                    return RedirectToAction("SimpleAd");
            }
        }

        [HttpGet("Ad/Car")]
        public IActionResult Car()
        {
            return View("Index", AdType.Car);
        }

        [HttpGet("Ad/RealEstateRental")]
        public IActionResult RealEstateRental()
        {
            return View("Index", AdType.RealEstateRental);
        }

        [HttpGet("Ad/SimpleAd")]
        public IActionResult SimpleAd()
        {
            return View("Index", AdType.SimpleAd);
        }

        [HttpGet("Ad/List/{type}")]
        public async Task<IActionResult> List(AdType type)
        {
            // var list = ... switch to retrieve list of ads via switch and generic data access methods 
            return list;
        }

        [HttpGet("Ad/{type}/Details/{id}")]
        public async Task<IActionResult> Details(AdType type, int id)
        {
            var ad = // ... switch by type to retrieve list of ads via switch and generic data access methods
            if (ad == null) return NotFound($"Ad not found.");

            // for instance - configure mappings via Automapper from DB entity to model views
            var model = _mapper.Map<AdViewModel>(ad);

            // Note: view will have to detect the exact ad instance type and show/hide corresponding UI fields
            return View(model);
        }

        [HttpGet("Ad/{type}/Add/")]
        public IActionResult Add(AdType type)
        {
            var ad = // ... switch by type to validate/add new entity  

            return View(_mapper.Map<AdEditModel>(ad));
        }

        [HttpPost("Ad/{type}/Add/")]
        public async Task<IActionResult> Add(AdEditModel model)
        {
            // detect ad type and save 
            return View(model);
        }

        [HttpGet("Ad/{type}/Edit/{id}")]
        public async Task<IActionResult> Edit(AdType type, int id)
        {
            // similar to Add
            return View(model);
        }

        [HttpPost("Ad/{type}/Edit/{id}")]
        public async Task<IActionResult> Edit(AdEditModel model)
        {
            // similar to Add
            return View(model);
        }

        // And so on
    }
}

但是我应该指出,与UI相关的代码中的继承最终导致的问题多于收益.代码变得更加复杂,难以维护和保持整洁.因此,即使您的所有ViewsControllers彼此之间的代码非常接近,也应该将它们分开.您可以在DI服务(也称为business logic)或类似层下面开始优化重复代码"的用法.

But I should note, that inheritance in UI related code eventually results into more problems than benefits. The code becomes more complex to maintain and keep it clean. So it makes more sense to keep all your Views and Controllers separate, even if they have the code very close to each other. You could start optimiziong the "repeated code" usage below the your DI services (aka business logic) or similar layer.

UI级别的repeated code问题应通过提取组件(又名controlspartial viewsview components)解决.控制器继承是可能的,但使代码难以维护.

The repeated code problem for UI level should be solved via extracting components (aka controls, partial views, view components). Controller inheritance is possible but make code harder to maintain.

这篇关于利用控制器和视图中的继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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