c# - 通用 View 和 Controller 以避免 MVC 中的重复代码

标签 c# asp.net-mvc generics interface

我的 MVC 项目中有很多具有相同基本结构的模型。因此,我创建了一个如下所示的大师类。

    public class MasterTemplate
    {
        [Key]
        public int Id { get; set; }

        [Required]
        [StringLength(255)]
        public string Description { get; set; }
        public DateTime? UpdatedOn { get; set; }
        public string UpdatedBy { get; set; }
    }

然后我创建了如下所示的所有模型类。

    public class Industry : MasterTemplate
    {

    }

    public class Caste : MasterTemplate
    {

    }

    public class Gender : MasterTemplate
    {

    }

    public class Qualification : MasterTemplate
    {

    }

    public class BloodGroup: MasterTemplate
    {

    }

还有很多这样的。以下是我的 IndustryController 代码。

 public class IndustryController : Controller
    {
        private ApplicationDbContext _context { get; set; }
        private string UserId { get; set; }

        public IndustryController()
        {
            _context = new ApplicationDbContext();
            UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
        }


        public ActionResult Index(int id = 0)
        {
            Industry data = new Industry();
            if (id > 0)
                data = _context.Industries.SingleOrDefault(c => c.Id == id);
            if (data == null)
                data = new Industry();

            return View("Industry", data);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Save(Industry data)
        {
            if (!ModelState.IsValid)
                return View("Industry", data);

            var record = _context.Industries.Where(c => c.Description.Trim().ToLower() == data.Description.Trim().ToLower() && c.Id != data.Id);
            if (record.Count() > 0)
            {
                ModelState.AddModelError("Duplicate Industry", "Industry already exist");
                return View("Industry", data);
            }

            Industry cm = new Industry();
            if (data.Id >= 1)
            {
                cm = _context.Industries.SingleOrDefault(c => c.Id == data.Id);
                cm.Description = data.Description;
                cm.UpdatedOn = DateTime.Now;
                cm.UpdatedBy = UserId;
            }
            else
            {
                cm = data;
                _context.Industries.Add(cm);
            }
            _context.SaveChanges();


            return RedirectToAction("Index", new { id = 0 });

        }

下面是我的 IndustryView 代码

@model Industry
@{
    ViewBag.Title = "Industries";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h3>Industries Management</h3>
<div class="row">
    <div class="col-md-4">
        @using (@Html.BeginForm("Save", "Industry"))
        {
            @Html.ValidationSummary("Please correct the following")
            @Html.HiddenFor(m => m.Id)

            <div class="form-group">
                <div>
                    @Html.LabelFor(m => m.Description)
                    @Html.TextBoxFor(m => m.Description, new { @class = "form-control", autocomplete = "off" })
                    @Html.ValidationMessageFor(m => m.Description)
                </div>
            </div>
            @Html.AntiForgeryToken()
            <button type="submit" class="btn btn-primary btn-sm">Save</button>
        }
    </div>
    <div class="col-md-8">
        <table class="table table-sm" id="mydata">
            <thead>
                <tr>
                    <th>
                        Industries
                    </th>
                    <th>

                    </th>
                </tr>
            </thead>
            <tbody></tbody>
        </table>
    </div>
</div>


@section scripts
{
    @Scripts.Render("~/bundles/jqueryval")
    <script>
        $(document).ready(function () {
            $("#mydata").DataTable({
                ajax: {
                    url: "/api/get/industries",
                    dataSrc: ""
                },
                columns: [
                    {
                        data: "description"
                    },
                    {
                        data: "id",
                        render: function (data) {
                            var url = '@Url.Action("Index", "Industry", new { id = "__data__" })';
                            return '<a href="' + url.replace('__data__', data) + '">Edit</a>';
                        }
                    }
                ]
            });
        });
    </script>
}

现在我的问题是,我项目中所有模型的 Controller 和 View 代码几乎相似。如上。因此,我想概括它们并创建一个可用于我所有其他模型的 Controller 和 View 。我是泛型的新手,尝试了以下代码,但仍然无法找到前进的方向。这让我很困惑。

    public interface IMaster
    {
        int Id { get; set; }
        string Description { get; set; }
    }

 public class GenericController : Controller
    {
        private ApplicationDbContext _context { get; set; }
        private string UserId { get; set; }

        public GenericController()
        {
            _context = new ApplicationDbContext();
            UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
        }


        public ActionResult Index(int id = 0)
        {
            IMaster data = null;
            if (id > 0)
                data = _context.Industries.SingleOrDefault(c => c.Id == id);
            if (data == null)
                data = new Industry();

            return View("Generic", data);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Save(IMaster data)
        {
            if (!ModelState.IsValid)
                return View("Generic", data);

            var record = _context.Industries.Where(c => c.Description.Trim().ToLower() == data.Description.Trim().ToLower() && c.Id != data.Id);
            if (record.Count() > 0)
            {
                ModelState.AddModelError("Duplicate Industry", "Industry already exist");
                return View("Generic", data);
            }

            Industry cm = new Industry();
            if (data.Id >= 1)
            {
                cm = _context.Industries.SingleOrDefault(c => c.Id == data.Id);
                cm.Description = data.Description;
                cm.UpdatedOn = DateTime.Now;
                cm.UpdatedBy = UserId;
            }
            else
            {
                cm.Id = data.Id;
                cm.Description = data.Description;
                _context.Industries.Add(cm);
            }
            _context.SaveChanges();


            return RedirectToAction("Index", new { id = 0 });

        }
    }

有人可以指导我正确的方向,需要为我项目中的所有类似模型创建一个通用 Controller 和 View 。

最佳答案

我还没有运行它,但我非常有信心,这应该可以解决问题!实际上,唯一真正通用的部分是 Controller 。其他的只是通常的多态性。谢谢你的灵感。考虑这样的解决方案很有趣。也许我将来会构建类似的东西。

请注意:您会将 Controller 的名称绑定(bind)到每个模型的名称。请注意这一点!有一个必须保留的命名模式,否则你会破坏它。

public class [ModelName]Controller : MasterController
{
}


ajax 端点将以 [PluralName] 的值结尾
(在 View 中继续阅读以了解我的意思。)

您将需要 MasterTemplate 中的附加属性。理想情况下将其抽象化,这样您就不会忘记在派生类中实现它。这是针对 View header 中的复数名称和 View 中的 ajax 调用。

public abstract class MasterTemplate
{
    [Key]
    public int Id { get; set; }

    public abstract string PluralName {get;}

    [Required]
    [StringLength(255)]
    public string Description { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public string UpdatedBy { get; set; }
}

行业会变成这样

public class Industry: MasterTemplate
{
  public override string PluralName => "Industries"
}

制作一个真正通用的 Controller 并从中派生所有其他 Controller

public class IndustryController : MasterController<Industry>
{
   //done everthing else is in the master :)
}

这里是通用的 MasterController

public class MasterController<T> : Controller where T : MasterTemplate, new()
{
    private ApplicationDbContext _context { get; set; }
    private string UserId { get; set; }

    public MasterController()
    {
        _context = new ApplicationDbContext();
        UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
    }


    public ActionResult Index(int id = 0)
    {
        T data = (id > 0) 
               ? data = _context.Set<T>().SingleOrDefault(c => c.Id == id) ?? new T() 
               : new T();

        return View("View", data);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Save(T data)
    {
        if (!ModelState.IsValid)
            return View("View", data);

        var record = _context.Set<T>().Where(c => c.Description.Trim().ToLowerInvariant() == data.Description.Trim().ToLowerInvariant() && c.Id != data.Id);
        if (record.Count() > 0)
        {
            ModelState.AddModelError($"Duplicate {typeof(T).Name}", $"{typeof(T).Name} already exist");
            return View("View", data);
        }

        if (data.Id >= 1)
        {
            T cm = _context.Set<T>().SingleOrDefault(c => c.Id == data.Id);
            cm.Description = data.Description;
            cm.UpdatedOn = DateTime.Now;
            cm.UpdatedBy = UserId;
        }
        else
        {
            _context.Set<T>().Add(data);
        }
        _context.SaveChanges();


        return RedirectToAction("Index", new { id = 0 });

    }

将 View 命名为“View”(或者与您在 MasterController 中的名称相同)并将其放置在共享文件夹中,以便每个 Controller 都能在那里找到它。

@model MasterTemplate
@{
    string name = Model.GetType().Name;
    ViewBag.Title = name;
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h3>@Model.PluralName Management</h3>
<div class="row">
    <div class="col-md-4">
        @using (@Html.BeginForm("Save", name))
        {
            @Html.ValidationSummary("Please correct the following")
            @Html.HiddenFor(m => m.Id)

            <div class="form-group">
                <div>
                    @Html.LabelFor(m => m.Description)
                    @Html.TextBoxFor(m => m.Description, new { @class = "form-control", autocomplete = "off" })
                    @Html.ValidationMessageFor(m => m.Description, $"{name} is required.", new { @class = "text-danger" })
                </div>
            </div>
            @Html.AntiForgeryToken()
            <button type="submit" class="btn btn-primary btn-sm">Save</button>
        }
    </div>
    <div class="col-md-8">
        <table class="table table-sm" id="mydata">
            <thead>
                <tr>
                    <th>
                        @(name)
                    </th>
                    <th>

                    </th>
                </tr>
            </thead>
            <tbody></tbody>
        </table>
    </div>
</div>


@section scripts
{
    @Scripts.Render("~/bundles/jqueryval")
    <script>
        $(document).ready(function () {
            $("#mydata").DataTable({
                ajax: {
                    url: "/api/get/@(Model.PluralName)",
                    dataSrc: ""
                },
                columns: [
                    {
                        data: "description"
                    },
                    {
                        data: "id",
                        render: function (data) {
                            var url = '@Url.Action("Index", "@(name)", new { id = "__data__" })';
                            return '<a href="' + url.replace('__data__', data) + '">Edit</a>';
                        }
                    }
                ]
            });
        });
    </script>
}

关于c# - 通用 View 和 Controller 以避免 MVC 中的重复代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55458505/

相关文章:

c# - 配置 IIS 或 Web.config 以仅提供特定文件夹中的所有文件

java - 关于SCJP-6考试的问题

java - 如何将 rxjava2 Zip 函数(从 Single/Observable)的数量概括为 n 个可选参数而不丢失其类型?

c# - Silverlight 5 双击总是将 ClickCount 返回为 1

c# - 使用 Func Delegate 进行错误检查

c# - SQLite - 如何摆脱恼人的跟踪消息 - "Native library pre-loader failed to get setting ... value"?

asp.net - ASP .Net MVC 5 不在新页面中加载 CSS 和 JS 文件

c# - Datagridview新行消失

asp.net-mvc - 如何在不引入依赖的情况下用 Onion Architecture 实现 UnitOfWork?

typescript - 具有泛型的类型别名表现出与非泛型类型不同的行为