我在尝试思考在控制器Action中重新创建数据库对象的最佳方法时遇到了麻烦。
我想使用ModelBinder,因此在我的操作中,我可以通过参数访问对象,而不必重复代码以根据标识符参数从数据库中获取对象。所以我想让一个ModelBinder执行对dataaccess层的调用以获取原始对象(或者如果数据库中不存在则创建一个新对象),然后将任何属性绑定到数据库对象以更新它。但是我已经读过ModelBinders不应该进行数据库查询(这个article的第一条评论)。
如果ModelBinder不应该执行数据库查询(所以只使用DefaultModelBinder)那么具有其他db对象属性的数据库对象呢?这些永远不会被分配。
在用户编辑对象后保存对象(在视图中可编辑1或2个属性),ModelBinded对象将丢失数据,因此保存它将导致数据库中的数据被无效值覆盖,或NOT-NULL约束失败。
那么,从数据库获取控制器操作中的对象的最佳方法是从视图中发回的表单数据绑定吗?
请注意我使用NHibernate。
答案 0 :(得分:4)
我从数据库中获取模型对象,然后在对象上使用UpdateModel(或TryUpdateModel)来更新表单参数中的值。
public ActionResult Update( int id )
{
DataContext dc = new DataContext();
MyModel model = dc.MyModels.Where( m => m.ID == id ).SingleOrDefault();
string[] whitelist = new string[] { "Name", "Property1", "Property2" };
if (!TryUpdateModel( model, whitelist )) {
... model error handling...
return View("Edit");
}
ViewData.Model = model;
return View("Show");
}
答案 1 :(得分:2)
不幸的是,您无法控制模型绑定器的构造,因此您无法注入任何存储库实现。
您可以直接联系服务定位器以提取您的存储库&获取项目:
public class ProductBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
if(modelType != typeof(Product))
return null;
var form = controllerContext.HttpContext.Request.Form;
int id = Int32.Parse(form["Id"]);
if(id == 0)
return base.CreateModel(controllerContext, bindingContext, modelType);
IProductRepository repository = ServiceLocator.Resolve<IProductRepository>();
return repository.Fetch(id);
}
}
如果您可以使用提供类的Id的基类或接口,您甚至可以为所有实体使用。
你必须在Global.asax中设置它:
ModelBinders.Binders.Add(typeof(Product), new ProductBinder());
然后你可以这样做:
public ActionResult Save([Bind] Product product)
{
....
_repository.Save(product);
}
答案 2 :(得分:0)
首先我要说明我不建议从ModelBinders访问数据库,因为从关注点分离的角度来看,ModelBinder只应负责解释客户端请求,显然数据库不是。
如果您不想重复自己(DRY),请使用存储库/服务
但是,如果你真的想这样做,那么
在global.asax.cs中将自定义MyModelBinderProvider注册到MVC
ModelBinderProviders.BinderProviders.Add(new EntityModelBinderProvider
{
ConnectionString = "my connection string"
));
修改自定义ModelBinderProvider以包含数据库设置
public class EntityBinderProvider: IModelBinderProvider
{
public string ConnectionString { get; set; }
public IModelBinder GetBinder(Type modelType)
{
if (Is known entity)
return new EntityBinder(ConnectionString);
else
return null;
}
}
遵循Ben Scheirman的进一步指示
答案 3 :(得分:-1)
您实际上不必点击数据库。只需设置对象的ID就足以设置关系,但要注意你的级联。确保您的级联设置不会更新相关对象,因为它将清除值。