我有一个ExtJs的编辑表单,但ExtJs与此问题无关。在提交时,我将表单绑定到模型。
public ActionResult EditUserHour([Bind(Include = "Id, Duration, HourCategoryId, Explanation, InvoiceTypeId, StartDate, StartTime, TicketId")] UserHour userHour)
{
if (ModelState.IsValid)
{
try
{
_service.Update(userHour);
}
catch (RuleException ex)
{
ex.CopyToModelState(ModelState, string.Empty);
}
}
if (ModelState.IsValid)
return Json(new { success = true, redirect = Url.Action(ListAction) });
else
return Json(new { success = false, errors = ModelState.ToDictionary() });
}
查看_service.Update(userHour);线。控制器和存储库之间是服务层,因此我不直接调用存储库。我认为这比在存储库中混合数据访问,验证和业务逻辑更好。
目前,_service中的 - not working - 方法是:
public void Update(UserHour userHour)
{
CRMDataContext c = new CRMDataContext();
c.Refresh(RefreshMode.KeepCurrentValues, userHour);
c.SubmitChanges();
}
我已经尝试了所有c.Attach(...)调用可能和刷新调用的所有内容,但是我得到了各种异常,比如附加一个已经存在密钥的对象。
我遇到的一个可能的解决方案是从datacontext中检索原始实体并简单地设置所有属性,但这远非一个简洁的解决方案。第二个选项是使用FormCollection映射到原始实体,但由于安全原因,我避免使用FormCollection并更喜欢模型绑定。此外,我无法想象模型绑定与更新不兼容。
是否有可能在控制器中使用模型绑定创建userHour,为其提供userHour的身份,它实际上是更新并将其存储在数据库中?它现在整天都在困扰着我。
非常感谢任何帮助!
答案 0 :(得分:0)
如果您正在使用视图模型或演示模型,那么您将不得不处理所有丑陋的左/右分配代码。它没有好办法。这就是DTO的本质。他们添加了一层抽象,很多时候他们都很有帮助,但是你必须处理后果。
但是,您的示例似乎表明您的数据库与视图之间存在一对一的关系。我们可以使用Attach()来利用它。这是如何做到的。
示例:
示例:
@Html.HiddenFor(x => x.Id)
@Html.HiddenFor(x => x.Timestamp)
示例:
public void Update(UserHour userHour)
{
var ctx = new CRMDataContext();
ctx.UserHours.Attach(userHour, true);
ctx.SubmitChanges();
}
答案 1 :(得分:0)
正如您所说,唯一的方法是获取原始对象并更新其属性。
您可以为Data.Linq.Table实现一个通用扩展,它接受您的模型绑定对象,并通过其主键从DataContext中检索原始项。在此之后,您使用refrection迭代所有属性,并将新值设置为检索到的实体。如果并非所有属性都通过模型绑定绑定并因此设置为默认值,则可以提供要更新的属性数组。
我知道Refelction不是一个好的解决方案,但它有时会有所帮助。
public static class TableExtensions
{
public static void UpdateOnSubmit<T>(this Table<T> table, T changes) where T : class
{
UpdateOnSubmit(table, changes, null);
}
public static void UpdateOnSubmit<T>(this Table<T> table, T changes, string[] properties) where T : class
{
var item = table.FirstOrDefault(GetPkExpression(table, changes));
if (item != null)
{
UpdateItem<T>(ref item, changes, properties);
}
}
private static void UpdateItem<T>(ref T original, T changes, string[] properties) where T : class
{
Type OriginalType = typeof(T);
if (properties == null)
{
PropertyInfo[] Info = OriginalType.GetProperties();
foreach (PropertyInfo PI in Info)
{
if (IsUpdateableColumn(PI))
{
PI.SetValue(original, PI.GetValue(changes, null), null);
}
}
}
else
{
foreach (string propName in properties)
{
PropertyInfo PI = OriginalType.GetProperty(propName);
if (PI != null && IsUpdateableColumn(PI))
{
PI.SetValue(original, PI.GetValue(changes, null), null);
}
}
}
}
private static bool IsUpdateableColumn(PropertyInfo pi)
{
object[] attributes = pi.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attributes.Length == 0)
return false;
foreach (ColumnAttribute attr in attributes)
{
if (attr.IsDbGenerated)
return false;
}
return true;
}
private static Expression<Func<T, bool>> GetPkExpression<T>(Table<T> table, T value) where T : class
{
var mapping = table.Context.Mapping.GetTable(typeof(T));
var pk = mapping.RowType.DataMembers.FirstOrDefault(d => d.IsPrimaryKey);
if (pk == null)
{
throw new Exception(string.Format("Table {0} does not contain a Primary Key field", mapping.TableName));
}
var pkValue = typeof(T).GetProperty(pk.Name).GetValue(value, null);
var param = Expression.Parameter(typeof(T), "e");
return Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(param, pk.Name), Expression.Constant(pkValue)), new ParameterExpression[] { param });
}
}
使用此扩展程序后,您的更新功能应如下所示:
public void Update(UserHour userHour)
{
CRMDataContext c = new CRMDataContext();
c.UserHour.UpdateOnSubmit(userHour, new string[] { "Id", "Duration", "HourCategoryId", "Explanation", "InvoiceTypeId", "StartDate", "StartTime", "TicketId" });
c.SubmitChanges();
}
如果要更新所有属性,则可以省略具有属性名称的数组。
答案 2 :(得分:0)