使用实体框架的读/写方法的约定。 Controller vs DbContext?

时间:2015-03-03 15:36:32

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

我正在为我正在构建的网站编写许多基本的CRUD视图和控制器逻辑。到目前为止,我已经在控制器中编写了大部分代码,其中包括通常的验证,输入 - 卫生和错误处理。我应该在控制器中编写所有数据库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,则有时需要PersonViewModel List<PetViewModel>Person要写入数据库:需要创建Pet以及每个PetController。但是,public ActionResult Create(PetViewModel vm)已经定义了PersonController,但我不能在Pet内部使用它来为数据库写一个新的Create(PetViewModel vm)。所以,我想将DbContext的db逻辑移到其他地方,我可以从其他控制器中访问它。我会在哪里移动它?是否适合将我的public static bool CreateHelper(DbContext db, PetViewModel vm)引用从控制器传递给静态帮助方法{{1}}?

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

您可以使用的一种模式是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 });
    }
}