【问题标题】:Convention for read/write methods with Entity Framework. Controller vs DbContext?实体框架的读/写方法约定。控制器与 DbContext?
【发布时间】:2015-03-03 15:36:32
【问题描述】:

我正在为我正在构建的网站编写大量基本的 CRUD 视图和控制器逻辑。到目前为止,我已经在控制器中编写了大部分代码,这包括通常的验证、输入清理和错误处理。我应该在我的控制器中编写我所有的 DB I/O 代码吗?或者我应该将一些移到我的 DbContext 吗?我问这个,因为我听说过控制器类与模型类之间应该发生多少冲突的观点?将 Db 上下文的实例传递出控制器是否合适?或者我应该在 DbContext 上使用扩展类吗?

例如:

public ActionResult Create(ThingViewModel vModel)
{
    try
    {
        if (ModelState.IsValid)
        {
            var nm = vModel.ToActualModel();

            nm.RelatedThing = nm.RelatedThingId == null ? 
                null : db.RelatedThings.Single(v => v.Id == nm.RelatedThingId);
            nm.UtcCreatedOn = DateTime.UtcNow;

            db.Thing.Add(nm);
            db.SaveChanges();

            var successMessage = "You have created a new Thing!";
            return RedirectToAction("Index", new { successMessage = successMessage });
        }
        else
        {
            ViewBag.EntityName = "Thing";
            ViewBag.ControllerName = "Thing";
            ViewBag.Title = "Admin | Thing - Create";

            return View("~/Views/Thing/Create.cshtml", vModel);
        }
    }
    catch(Exception e)
    {
        var errorMessage = "An error occured when creating a new thing!";
        return RedirectToAction("Index", new { errorMessage = errorMessage });
    }
}

这应该变成:

public ActionResult Create(ThingViewModel vModel)
{
    try
    {
        if (ModelState.IsValid)
        {
            db.CreateNewThing(vModel) // Defined elsewhere

            var successMessage = "You have created a new Thing!";
            return RedirectToAction("Index", new { successMessage = successMessage });
        }
        else
        {
            ViewBag.EntityName = "Thing";
            ViewBag.ControllerName = "Thing";
            ViewBag.Title = "Admin | Thing - Create";

            return View("~/Views/Thing/Create.cshtml", vModel);
        }
    }
    catch(Exception e)
    {
        var errorMessage = "An error occured when creating a new thing!";
        return RedirectToAction("Index", new { errorMessage = errorMessage });
    }
}

在 DbContext 内部:

 public bool CreateNewThing(ThingViewModel vModel)
 {
     //Thing Creation logic
     Things.Add(thing);
     SaveChanges();
 }

为了澄清,我想为每个实体编写一次创建/编辑/删除逻辑,并能够在其他控制器中使用它们。因此,如果我有PersonPet 实体以及PersonControllerPetController,则有时需要将包含List<PetViewModel>PersonViewModel 写入数据库:Person 需要与每个Pet 一起创建。但是,PetController 已经定义了public ActionResult Create(PetViewModel vm),但我不能从PersonController 内部使用它来将新的Pet 写入数据库。所以,我想将Create(PetViewModel vm) 的数据库逻辑移到其他地方,我可以从其他控制器内部访问它。我要把它移到哪里?将我的DbContext 的引用从控制器传递给静态辅助方法public static bool CreateHelper(DbContext db, PetViewModel vm) 是否合适?

【问题讨论】:

  • 绝对不要抽象为Things.Add(thing);,这就是所谓的存储库模式,理智的人认为它是一种反模式。
  • Things.Add(thing) 已在 DbContext 类中定义,我不会编写它。您是否熟悉 EntityFramework ORM,因为我很确定它本质上实现了存储库模式。
  • 是的。确实如此,这就是为什么在它之上实现自己的存储库是零意义的,抱歉,在我的第一条评论中,我的意思是说将 EF 抽象到存储库中是一种反模式。
  • 有道理,然后从它的声音中,您确认了我最初的预感,以使助手远离我的 DAL。但我想知道是否应该将它们放在关联的控制器中,或者作为单独的辅助类。
  • 我有一个 C# 聊天室中的一个人写的东西的链接,它基本上是 EF 的一个小包装器,让您可以控制哪些集合是可访问的。你可以看看here。这是一个工作单元实现,如果您以前听说过,它与 EF 配合得很好。

标签: c# asp.net asp.net-mvc entity-framework


【解决方案1】:

第一个是正确的。请记住,它是一个 MVC 模式 - 模型 - 视图 - 控制器。您的控制器正在做它应该做的事情,包括使用 dbcontext 从数据库中检索信息(这反过来又在做它应该做的事情)。它仍然遵循分离关注点的良好 MVC 实践,但不会以过度复杂化为代价。几年前,当我觉得“正确”比使用我的常识更重要时,我很难学会这一点。将 CreateNewThing 放在 db 上下文中不会给您带来真正的好处,而且在更大更复杂的应用程序中只会使水变得混乱。

【讨论】:

  • 但是现在,在从一个控制器内部修改相关实体时,我有一些重复的创建逻辑。我在原始问题的底部添加了一些说明。
  • 如果您需要定期将 Persons 和 Pets 视为一个组合,您可能需要考虑重构您的应用程序。再一次,直接模型到 DB 表的映射是没问题的,只要它服务于您的应用程序的目的。在我看来,一个更复杂的模型是人物和宠物的组合 (PeopleAndPets) 将包含两者,并且单个 PeoplAndPets 控制器将处理两者的每个加上混合。这是需要根据应用的用途和最常见的使用场景做出的判断调用之一。
【解决方案2】:

我认为在 MVC 项目中创建不一定是控制器的辅助类是很好的,但它不应该与您的数据层混合。 db.CreateNewThing(vModel) 应该避免。 db 用于数据存储和检索,而不是用于转换模型或做类似的事情。

【讨论】:

    【解决方案3】:

    您可以使用的一种模式是command pattern。这样您就可以将业务逻辑与控制器分离。

    另见:Command, CommandHandler and CommandInvoker

    样板:

    public interface ICommandResponse
    {
        bool Success { get; }
        IReadOnlyCollection<string> Errors { get; }
    }
    
    public class CommandResponse : ICommandResponse
    {
        public bool Success { get; set; }
        public IReadOnlyCollection<string> Errors { get; set; }
    }
    
    public interface ICommand<in TModel>
    {
        ICommandResponse Handle(TModel model);
    }
    
    public abstract class CommandBase<TModel> : ICommand<TModel>
    {
        public abstract ICommandResponse Handle(TModel model);
    
        protected ICommandResponse Success()
        {
            return new CommandResponse { Success = true };
        }
    
        protected ICommandResponse Fail(params string[] errors)
        {
            return new CommandResponse { Errors = new ReadOnlyCollection<string>(errors) };
        }
    }
    

    代码:

    public class CreateThingCommand : CommandBase<ThingViewModel>
    {
        private readonly DbContext _context;
    
        public CreateThingCommand(DbContext context)
        {
            _context = context;
        }
    
        public override ICommandResponse Handle(ThingViewModel viewModel)
        {
            var model = viewModel.ToActualModel();
    
            model.RelatedThing =
                model.RelatedThingId == null
                ? null
                : _context.RelatedThings.Single(v => v.Id == model.RelatedThingId);
            model.UtcCreatedOn = DateTime.UtcNow;
    
            _context.Thing.Add(model);
            _context.SaveChanges();
    
            return Success();
        }
    }
    
    // Inside controller
    public ActionResult Create(ThingViewModel vModel)
    {
        try
        {
            if (ModelState.IsValid)
            {
                var result = createThingCommand.Handle(vModel);
    
                if(result.Success)
                {
                    var successMessage = "You have created a new Thing!";
                    return RedirectToAction("Index", new { successMessage = successMessage });
                }
    
                // Handle error
                return RedirectToAction("Index", new { failMessage = "Something went wrong" });
            }
    
            ViewBag.EntityName = "Thing";
            ViewBag.ControllerName = "Thing";
            ViewBag.Title = "Admin | Thing - Create";
    
            return View("~/Views/Thing/Create.cshtml", vModel);
        }
        catch(Exception e)
        {
            var errorMessage = "An error occured when creating a new thing!";
            return RedirectToAction("Index", new { errorMessage = errorMessage });
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-26
      • 2022-12-12
      • 2012-05-30
      相关资源
      最近更新 更多