只是想我会把它添加到组合中,因为我遇到了这篇文章 - 并使用了 Ladislav 建议的解决方案,并进行了一些更改:
public T Save<T>(T entity) where T : class {
try {
this.Set<T>().Attach(entity);
} catch {
// You may wish to add logging here, instead of throwing away the exception
}
try {
this.Entry<T>(entity).State = EntityState.Modified;
this.SaveChanges();
this.Entry<T>(entity).Reload(); // Update any server-generated fields
this.Detach<T>(entity); // Detach the object again, to avoid collisions
} catch { return null; }
return entity; // Return the updated version of the object.
}
internal void Detach<T>(T entity) where T : class {
ObjectContext.Detach(entity);
}
public ObjectContext ObjectContext {
get { return ((IObjectContextAdapter)this).ObjectContext; }
}
让我明确指出这些更改,以及我做出这些更改的原因。
首先 - 有 2try/catch
个块。第一个确保如果对象已经附加,我们不会出错。你实际上不需要对错误做任何事情——它是信息性的,而不是关键的。所以——如果它发生就忽略它——然后继续。
第二个try/catch
块优雅地处理任何其他类型的保存错误。例如,如果您的更改会违反外键约束 - 此块将捕获失败。
第三件事是我添加了一个Detach<T>()
方法。这也是为了防止碰撞.Attach()
。本质上,我的假设是,如果您正在编写像这样首先需要.Attach()
对象的通用方法 - 这是因为您使用的是没有更改跟踪的 POCO。既然如此 - 在你完成保存/更新之后 - 你应该再次分离是有道理的。
为了完整起见——我还应该指出,如果你真的想明确地捕获“对象已附加”错误——你应该InvalidOperationException
在第一个catch
块中捕获。
更好的方法
如果您确实有能力添加基类或接口(如果您使用.tt
生成的对象,则应该这样做),您可以通过如下替换第一行来提高此解决方案的类型安全性:
public void Save<T>(T entity) where T : BaseClass {
在我的应用程序的 EF5POCO.tt
文件中,我将其更改为为每个对象输出一个基类,并添加了一些序列化支持和其他一些调整。它的核心来自以下几行:
/* THIS SECTION ADDED IN ORDER TO CREATE A GLOBAL BASE CLASS */
fileManager.StartNewFile("EntityBase.cs");
#>
using System.Runtime.Serialization;
<# BeginNamespace(code); #>
<# foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) { #>
[KnownType(typeof(<#=code.Escape(entity)#>))]
<# } #>
public abstract class EntityBase { }
<#
EndNamespace(code);
/* END INJECTED BASE CLASS */
然后,我还将.tt
'sEntityClassOpening()
函数更新为如下所示:
public string EntityClassOpening(EntityType entity) {
String baseType = _typeMapper.GetTypeName(entity.BaseType);
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", String.IsNullOrWhiteSpace(baseType) ? "EntityBase" : baseType)
);
}
结果是我所有尚未继承的 POCO 对象现在都派生自EntityBase
- 这允许我将Save<T>
签名更改为:
public T Save<T>(T entity) where T : EntityBase {
...现在我的代码在编译时是类型安全的。
那么 - 你如何有效地使用它?
请记住,如果失败,该.Save<T>()
方法将返回null
,如果成功,则返回实体的更新版本。我一般这样称呼它:
if (null == (entity = db.Save(entity))
throw new Exception( ... );
// If we got this far, it's because the save succeeded.
DoSomething().With(entity);