asp.net-mvc - 利用 Controller 和 View 中的继承

标签 asp.net-mvc oop inheritance repository unit-of-work

我发布了this review不久前在 codereview.stackexchange.com 上...我觉得它可能更适合 stackoverflow,因为它更多的是一个问题而不是代码审查。

需要一些解释,请耐心等待

<小时/>

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

我使用继承来定义我的广告类型,这个问题是关于利用分层结构删除 Controller 和 View 中的重复代码。

我有不同的广告类型:SimpleAd , CarRealEstateRental .

每个广告都源自 AdBase,它具有所有共同属性:

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; }
}

现在其他广告均从此基类派生:

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 与数据库交互,并且我正在使用工作单元和存储库模式:

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

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...
}

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

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

到目前为止,我对一切都很满意...但问题是我不知道如何在 Controller 和 View 中利用这种继承层次结构。

目前,我有 3 个 Controller :SimpleAdController , CarControllerRealEstateRentalController :

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 我有):

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

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

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
         */
    }
}

如果我执行上述操作,那么我的广告 Controller 可以继承它,并且我不需要在每个 Controller 中重复相同的显示方法...但随后我需要创建我的 UnitOfWork通用...或有 2 个 UoW(通用和非通用)...我不确定这是否是一个好主意? 关于 AdBaseController 的任何建议

<小时/>

同样,我在 View 中重复了很多代码。例如,这是显示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)

我再次感觉我在每个 View 中重复了很多代码。我尝试通过将重复代码放入公共(public)部分 View 中来减少重复代码的数量。 我不确定是否有更好的方法来做到这一点?

最佳答案

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

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 服务(又名业务逻辑)或类似层下的“重复代码”使用。

UI级别的重复代码问题应该通过提取组件来解决(又名控件部分 View View 组件)。 Controller 继承是可能的,但会使代码更难维护。

关于asp.net-mvc - 利用 Controller 和 View 中的继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52284912/

相关文章:

javascript - 从 document.ready 调用 jQuery 插件抛出 getPreventDefault 错误

c# - 从 RouteData 生成格式化的友好 URL

java - 接口(interface)比抽象类有什么优势?

c# - 继承的Window不能有名字吗?

javascript - 我对在哪里实例化对象感到困惑

asp.net-mvc - 使用 Linq2Sql 更新数据

asp.net-mvc - Ajax 请求 MVC4 的时间很长 "Wait"

c# - 在 C# 中实现多态性,如何最好地做到这一点?

python - python中包的全局变量可以被认为是邪恶的吗?

java - 为什么使用抽象类比使用普通类更好地继承?