我想在数据库中更新一个对象。我是EF的新手,但已经做了很多阅读。显然我的方法是错的,但我不明白为什么。 FYI整个引用的Context是一个ObjectContext,它在此代码开始时被新实例化,并在之后立即处理。这是我的Update方法 - View是我想在数据库中更新的对象,它有4个ICollection属性,我希望将这些属性保存到数据库中:
public void Update(View view)
{
var original = Read(view.Username, view.ViewId);
original.ViewName = view.ViewName;
ProcessChanges<CostCentre, short>(Context.CostCentres, original.CostCentres, view.CostCentres, "iFinanceEntities.CostCentres", "CostCentreId");
ProcessChanges<LedgerGroup, byte>(Context.LedgerGroups, original.LedgerGroups, view.LedgerGroups, "iFinanceEntities.LedgerGroups", "LedgerGroupId");
ProcessChanges<Division, byte>(Context.Divisions, original.Divisions, view.Divisions, "iFinanceEntities.Divisions", "DivisionId");
ProcessChanges<AnalysisCode, short>(Context.AnalysisCodes, original.AnalysisCodes, view.AnalysisCodes, "iFinanceEntities.AnalysisCodes", "AnalysisCodeId");
int test = Context.SaveChanges();
}
首先,我从数据库中获取原始数据,因为我想将其集合与新的集合进行比较。这应该确保添加和删除正确的子对象。我使用这个ProcessChanges方法依次比较每个集合:
private void ProcessChanges<TEntity, TKey>(ObjectSet<TEntity> contextObjects, ICollection<TEntity> originalCollection, ICollection<TEntity> changedCollection, string entitySetName, string pkColumnName)
where TEntity : class, ILookupEntity<TKey>
{
List<TKey> toAdd = changedCollection
.Select(c => c.LookupKey)
.Except(originalCollection.Select(o => o.LookupKey))
.ToList();
List<TKey> toRemove = originalCollection
.Select(o => o.LookupKey)
.Except(changedCollection.Select(c => c.LookupKey))
.ToList();
toAdd.ForEach(a =>
{
var o = changedCollection.Single(c => c.LookupKey.Equals(a));
AttachToOrGet<TEntity, TKey>(entitySetName, pkColumnName, ref o);
originalCollection.Add(o);
});
toRemove.ForEach(r =>
{
var o = originalCollection.Single(c => c.LookupKey.Equals(r));
originalCollection.Remove(o);
});
}
这会将新集合与旧集合进行比较,并确定要添加的对象和要删除的对象。请注意,集合都包含实现ILookupEntity的对象。
我的问题出现在我调用AttachToOrGet的行上。我从stackoverflow上的其他地方获得的这个方法。我正在使用它,因为我经常收到一条消息,说“附加一个新的子对象时,ObjectStateManager中已经存在一个具有相同键的对象”。当我发布下面这个方法的代码时,希望你能理解我对此的困惑:
public void AttachToOrGet<TEntity, TKey>(string entitySetName, string pkColumnName, ref TEntity entity)
where TEntity : class, ILookupEntity<TKey>
{
ObjectStateEntry entry;
// Track whether we need to perform an attach
bool attach = false;
if (Context.ObjectStateManager.TryGetObjectStateEntry(new EntityKey(entitySetName, pkColumnName, entity.LookupKey), out entry))
//if (Context.ObjectStateManager.TryGetObjectStateEntry(Context.CreateEntityKey(entitySetName, entity), out entry))
{
// Re-attach if necessary
attach = entry.State == EntityState.Detached;
// Get the discovered entity to the ref
entity = (TEntity)entry.Entity;
}
else
{
// Attach for the first time
attach = true;
}
if (attach)
Context.AttachTo(entitySetName, entity);
}
基本上这是说如果实体尚未附加,则附加它。 但是我的代码在Context.ObjectStateManager.TryGetObjectStateEntry行返回false,但在最后一行抛出异常,并在“ObjectStateManager中已存在具有相同键的对象”。对我而言是矛盾的。
就我而言,我正在努力实现一些非常简单的事情。编写存储过程需要20分钟的东西。简单的数据库更新。坦率地说,我不关心什么是附加的,什么不是,因为我不希望跟踪更改或创建代理或延迟加载或做任何其他EF提供给我。我只想采用一个非常简单的对象,并使用服务器之间的最小行程更新数据库。这怎么这么复杂?请有人帮帮我 - 我已经花了一整天的时间!
的更新 的
这是我的ILookupEntity类:
public interface ILookupEntity<TKey>
{
TKey LookupKey { get; }
string DisplayText { get; }
}
以下是它在CostCentre中的实现方式:
public partial class CostCentre : IFinancialCode, ILookupEntity<short>
{
#region IFinancialCode Members
public short ID { get { return CostCentreId; } }
public string DisplayText { get { return string.Format("{0} - {1}", Code, Description); } }
#endregion
#region ILookupEntity Members
public short LookupKey
{
get { return ID; }
}
#endregion ILookupEntity Members
}
答案 0 :(得分:1)
嗯,我已经完成了这个并找到了解决方案,但我不能说我理解它。当我在@Slauma发表评论后进行检查时,关键因素出现了。我想检查我是否使用了正确的实体集名称等,所以我在AttachToOrGet方法的顶部附近包含以下行:
var key = new EntityKey(entitySetName, pkColumnName, entity.LookupKey);
object temp;
if (!Context.TryGetObjectByKey(key, out temp))
throw new Exception(string.Format("No entity was found in {0} with key {1}", entitySetName, entity.LookupKey));
奇怪的是,这一点解决了这个问题。出于某种原因,一旦我调用TryGetObjectByKey,ObjectStateManager.TryGetObjectStateEntry调用实际上开始定位附加的实体。神奇。如果有人能解释清楚,我会喜欢它。
顺便说一句,我还需要包含以下代码,但这只是因为在我的情况下,建模实体位于与上下文本身不同的程序集中。
Assembly assembly = typeof(CostCentre).Assembly;
Context.MetadataWorkspace.LoadFromAssembly(assembly);