通用视图和控制器,以避免在MVC中重复代码

时间:2019-04-01 15:21:07

标签: 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>
}

现在我的问题是,我项目中所有模型的控制器和视图代码几乎都是相似的。如上。因此,我想对其进行概括,并创建一个可用于所有其他模型的控制器和视图。我是泛型新手,尝试了以下代码,但仍无法弄清楚前进的方向。对我来说真是令人困惑。

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

        }
    }

有人可以指导我正确的方向,需要创建一个通用控制器并查看我项目中所有相似的模型。

1 个答案:

答案 0 :(得分:1)

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

警告:将控制器的名称绑定到每个Model的名称。请注意这一点!必须保留一个命名模式,否则您将破坏它。

公共类[ModelName] Controller:MasterController
{
}

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

您将在MasterTemplate中需要一个附加属性。理想情况下,使其抽象化,因此您将不会忘记在派生类中实现它。这用于视图标题中的复数名称和视图中的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"
}

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

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

    }

将视图命名为“视图”(或在MasterController中将其命名为相同的视图)并将其放置在共享文件夹中,以供每个控制器在其中找到。

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