gpt4 book ai didi

c# - 通过记录对象的新实例更新 TableServiceContext 中的记录?

转载 作者:行者123 更新时间:2023-12-03 01:57:56 26 4
gpt4 key购买 nike

我有一个 azure 表记录对象定义为

[DataServiceKey("PartitionKey", "RowKey")]
public class TableRecord
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public DateTime Timestamp { get; set; }
public string Data { get; set; }
}

该记录用作存储库基础结构的一部分,该基础结构接受业务逻辑级别的数据对象,并将其序列化到 Data 属性中,然后将代码保存到表存储中,并在返回到客户端之前对其进行反序列化,因此业务逻辑对记录以及 PartitionKey 和 RowKey 一无所知。

这是存储库方法

public TEntity RegisterSave<TEntity>(TEntity entity, bool createNew)
{
var storeRec = _strategy.GetStoreRecord(entity);
if (createNew)
_context.AddObject(storeRec.TableName, storeRec.Record);
else
{
try
{
_context.AttachTo(storeRec.TableName, storeRec.Record, "*");
}
catch (InvalidOperationException)
{
// AttachTo can throw an exception if the entity is already being tracked.
// Ignore
}

_context.UpdateObject(storeRec.Record);
}
return entity;
}

_strategy 负责将实体类型正确映射到表名称,以及使用键正确创建 TableRecord 并将实体序列化到记录中。 storeRec.Record 属性是 TableRecord 类的实例。这种方法对于创建新记录和阅读记录非常有效。

但是当我尝试使用新数据更新现有记录时,更新失败,提示它无法更新上下文未跟踪的实体。尽管如果要在调试器中单步执行代码,结果会发现实际发生了两个异常 - 首先是在 AttachTo 方法中,该方法提示具有相同键的实体正在被跟踪,然后紧接着发生UpdateObject 提示它不是。

我哪里出错了?

明白了

好的,在 ilspy 的帮助下,我找到了问题的根本原因。 DataServiceContext 为加载到上下文的实体维护两个字典。一个字典的键是实体本身,另一个字典的键是实体 id,它本质上是实体 url。在 AttachTo 方法中,上下文会验证两个字典,如果在其中任何一个字典中找到条目,则抛出 InvalidOperationException 异常。但是UpdateObject方法只验证key是实体本身的字典,如果找不到则失败。

看来 DataServiceContext 假设只能对同一实体进行修改,默认情况下它不支持实体将被新实例整体替换。但逻辑使用带有默认比较器的标准字典类,因此在为 TableRecord 实现 IEquatable 接口(interface)后,一切都完美运行。

所以对我来说,解决方案是:

[DataServiceKey("PartitionKey", "RowKey")]
public class TableRecord: IEquatable<TableRecord>
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public DateTime Timestamp { get; set; }
public string Data { get; set; }

public bool Equals(TableRecord other)
{
if (other == null)
return false;
return PartitionKey.Equals(other.PartitionKey) && RowKey.Equals(other.RowKey);
}

public override bool Equals(object obj)
{
return Equals(obj as TableRecord);
}

public override int GetHashCode()
{
return PartitionKey.GetHashCode() ^ RowKey.GetHashCode();
}
}

最佳答案

解决这个问题的方法是,如果有一个现有实体,您需要 Detach() 它并 AttachTo() 您的新实体。然后进行您想做的更新。

我编写了一些代码来执行此操作。它还避免引发异常,尽管我不确定哪种方法更快。

        /// <summary>
/// Detach any existing rows with the same keys (if necessary), then attach to this object using the "*" ETag
/// </summary>
/// <param name="newEntity"></param>
protected virtual void SafeAttach(TableServiceEntity newEntity)
{
TableServiceEntity entity = GetExistingRow(newEntity.PartitionKey, newEntity.RowKey);
if(entity != null)
{
base.Detach(entity);
}

base.AttachTo("MY_TABLE_NAME_GOES_HERE", newEntity, "*");
}

private TableServiceEntity GetExistingRow(string partitionKey, string rowKey)
{
var query = (from e in base.Entities
where e.Entity is TableServiceEntity
&& ((TableServiceEntity)e.Entity).RowKey == rowKey
&& ((TableServiceEntity)e.Entity).PartitionKey == partitionKey
select (TableServiceEntity)e.Entity);

RetrierFunctionResult<TableServiceEntity> r = StorageOperationRetrier.Execute(() =>
{
return query.FirstOrDefault();
});

return r.Result;
}

要使用此功能,您可以将 try/catch block 替换为对 SafeAttach(storeRec) 的调用。

请注意,这种方法会破坏表存储的内置并发检查。你基本上得到了最后写入获胜的行为。这可能可以接受,也可能不可以接受,这取决于您的情况。

此外,如果您打算继续工作,您可能需要设置 MergeOption.NoTracking 。无论如何,您基本上都是在模拟该行为,并且禁用实体跟踪具有一些性能优势。

关于c# - 通过记录对象的新实例更新 TableServiceContext 中的记录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9603016/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com