【问题标题】:Generic CRUD controllers and views通用 CRUD 控制器和视图
【发布时间】:2015-01-17 06:51:00
【问题描述】:

我只是在浏览一些 ASP.NET 的介绍性教程,并且对如何实现一个简单的 CRUD 管理应用程序有了一个不错的想法。

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

/List/Model
/Edit/Model/id
/Update/Model/id
/Delete/Model/id

可以处理任何模型。

【问题讨论】:

  • 这篇 SO 帖子很好地讨论了我认为您正在尝试做的事情 stackoverflow.com/questions/1124877/…
  • 谢谢。我认为不同的人有不同的用例,我从过去的经验和我们为其他平台构建的解决方案中知道,通用解决方案对我们很有效。

标签: asp.net-mvc


【解决方案1】:

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

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

    ...
}

换句话说,您只需构建一个完整的可重用控制器结构,关键部分是您使用的是通用的TEntity 而不是具体的类。请注意,TEntity 定义为 IEntity, class, new()。这做了几件事。首先,class 允许您将其视为具体类型,new() 表示该类型将是可以实例化的东西,而不是抽象类之类的东西。 IEntity 只是您在应用程序中可能使用的任何内容的占位符,以确保所有类型都有一些共同点。至少对于 CRUD 样式的应用程序,您需要它来访问 Id 或类似的属性,例如您的编辑和删除操作。说TEntity 实现IEntity 可以让您利用IEntity 上的任何属性。如果您在此处使用具体类型而不是接口,则可以省略 class 部分,例如where TEntity : Entity, new().

然后,为了使用它,您只需定义一个继承自 AdminController&lt;&gt; 的新控制器并指定您正在使用的类型:

public class WidgetController : AdminController<Widget>
{
    public WidgetController(ApplicationDbContext context)
    {
        this.context = context;
    }
}

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

public WidgetController()
{
    this.context = new ApplicationDbContext();
}

但是,我建议您通常考虑使用依赖注入。另外,为了便于解释,我在这里直接使用上下文,但通常你会在这里使用服务、存储库等。

最后,如果您发现需要自定义 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()
    {
    }

    ...

然后:

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

    protected override void BeforeReturnView()
    {
        ViewBag.MySelectList = new List<SelectListItem>
        {
            ...
        };
    }
}

换句话说,您的基本操作方法中有一个钩子,您可以重写它来更改特定的功能,而不必重写整个操作本身。

您还可以进一步包括视图模型之类的内容,您可以在其中将泛型类定义扩展为:

 public abstract class AdminController<TEntity, TEntityViewModel, TEntityCreateViewModel, TEntityUpdateViewModel>
     where TEntity : IEntity, class, new()
     where TEntityViewModel : class, new()
     ...

然后:

public class WidgetController : AdminController<Widget, WidgetViewModel, WidgetCreateViewModel, WidgetUpdateViewModel>
{
    ...
}

这完全取决于您的应用程序需要什么。

【讨论】:

  • 谢谢,很有帮助。
  • 谢谢 Chris,其他几篇文章的很好的扩展,我会把它放在手边 :-)
  • @Chris,我欠你很多啤酒,因为你用你的好答案拯救了我,这次也不例外。塔。
  • 我看不到在Create 方法中使用Bind 属性的直接方法。那么这仍然足够安全吗?你对此有什么想法吗? public ActionResult Create([Bind(Include = "Id,SkillName,RatingNumber")] Skill skill)
  • 这是个问题。但是,您可以通过不使用Bind 来回避它,无论如何都是推荐的。如果您只打算使用实体上的属性子集,则应该使用视图模型来表示该子集。 Bind 有很多问题。最大的问题是您必须列出要编辑的所有属性(基于实体,可能是一个实质性列表)并将所有这些属性作为字符串。现在你有别的东西要维护。添加属性?删除了一个属性?更改了属性的名称?最好记得更改所有Binds。
猜你喜欢
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多