У меня есть объект записи таблицы 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 поддерживает два словаря для сущностей, загруженных в контекст. Ключи одного словаря - это сама сущность, а ключом другого - идентификатор сущности, который по сути является URL-адресом сущности. В методе AttachTo контекст проверяет оба словаря и выдает InvalidOperationException
, если запись найдена в любом из них. Но метод UpdateObject проверяет только словарь, в котором ключ является самой сущностью, и завершается ошибкой, если не найден.
Похоже, DataServiceContext предполагает, что изменения могут быть выполнены только для одной и той же сущности, по умолчанию он не поддерживает замену этой сущности новым экземпляром в целом. Но логика использует стандартный словарный класс с компаратором по умолчанию, поэтому после реализации интерфейса IEquatable для TableRecord все работало отлично.
Так что для меня решение было:
[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();
}
}