通用CRUD控制器和视图

pix*_*ike 6 asp.net-mvc

我刚刚学习了一些ASP.NET的入门教程,我对如何实现一个简单的CRUD管理应用程序有了一个很好的想法.

是否有任何常用的模式来实现通用的列表/创建/更新/删除操作?为每个模型构建脚手架,然后维护所有添加,编辑和列表视图和控制器似乎相当繁琐.实现通用操作会更有效,更不容易出错,例如:

/List/Model
/Edit/Model/id
/Update/Model/id
/Delete/Model/id
Run Code Online (Sandbox Code Playgroud)

可以处理任何模型.

Chr*_*att 15

我认为,在我构建的管理应用程序中,我已经做了类似的事情.基本上,关键是使用泛型.换句话说,您创建一个控制器,如:

public abstract class AdminController<TEntity> : Controller
    where TEntity : IEntity, class, new()
{
    protected readonly ApplicationDbContext context;

    public virtual ActionResult Index()
    {
        var entities = context.Set<TEntity>()
        return View(entities);
    }

    public virtual ActionResult Create()
    {
        var entity = new TEntity();
        return View(entity);
    }

    [HttpPost]
    public virtual ActionResult Create(TEntity entity)
    {
        if (ModelState.IsValid)
        {
            context.Set<TEntity>().Add(entity);
            context.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(entity);
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

换句话说,您只需构建一个完整的可重用控制器结构,关键部分是您使用泛型TEntity而不是具体类.请注意,TEntity定义为IEntity, class, new().这做了一些事情.首先,class允许您将其视为具体类型,并且new()意味着类型将是可以实例化的东西,而不是类似抽象类的东西.IEntity只是您在应用程序中使用的任何占位符,以确保所有类型都有一些共同点.至少对于CRUD风格的应用程序,您需要使用它来访问Id编辑和删除操作等内容或类似属性.说TEntity实现IEntity允许您使用任何属性IEntity.如果您在此使用混凝土类型而不是界面,则可以省略class零件,例如where TEntity : Entity, new().

然后,为了使用它,您只需定义一个新的控制器,该控制器继承AdminController<>并指定您正在使用的类型:

public class WidgetController : AdminController<Widget>
{
    public WidgetController(ApplicationDbContext context)
    {
        this.context = context;
    }
}
Run Code Online (Sandbox Code Playgroud)

这可能是您个人控制器所需要的全部内容.此外,值得注意的是,我已经将其设置为为您的上下文使用依赖注入.您可以随时将构造函数更改为:

public WidgetController()
{
    this.context = new ApplicationDbContext();
}
Run Code Online (Sandbox Code Playgroud)

但是,我建议你一般都要考虑使用依赖注入.此外,我在这里直接使用上下文是为了便于解释,但通常你会在这里使用服务,存储库等.

最后,如果您发现需要自定义CRUD操作的某些部分,但不一定需要自定义整个部分,您始终可以添加方法作为扩展点.例如,假设您需要填充一个特定实体的选择列表,您可能会执行以下操作:

public abstract class AdminController<TEntity> : Controller
    where TEntity : IEntity, class, new()
{
    ...

    public virtual ActionResult Create()
    {
        var entity = new TEntity();
        BeforeReturnView();
        return View(entity);
    }

    ...

    protected virtual void BeforeReturnView()
    {
    }

    ...
Run Code Online (Sandbox Code Playgroud)

然后:

public class WidgetController : AdminController<Widget>
{
    ...

    protected override void BeforeReturnView()
    {
        ViewBag.MySelectList = new List<SelectListItem>
        {
            ...
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

换句话说,您在基本操作方法中有一个钩子,您可以覆盖它以仅更改该特定功能位而不必覆盖整个操作本身.

您还可以进一步包含视图模型之类的内容,您可以将通用类定义扩展为:

 public abstract class AdminController<TEntity, TEntityViewModel, TEntityCreateViewModel, TEntityUpdateViewModel>
     where TEntity : IEntity, class, new()
     where TEntityViewModel : class, new()
     ...
Run Code Online (Sandbox Code Playgroud)

然后:

public class WidgetController : AdminController<Widget, WidgetViewModel, WidgetCreateViewModel, WidgetUpdateViewModel>
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

这一切都取决于您的应用程序需求.