Pergunta
Acabei de consertar um bug em um framework que estou desenvolvendo.O pseudo-pseudocódigo fica assim:
myoldObject = new MyObject { someValue = "old value" };
cache.Insert("myObjectKey", myoldObject);
myNewObject = cache.Get("myObjectKey");
myNewObject.someValue = "new value";
if(myObject.someValue != cache.Get("myObjectKey").someValue)
myObject.SaveToDatabase();
Então, essencialmente, eu estava obtendo um objeto do cache e, posteriormente, comparando o objeto original com o objeto armazenado em cache para ver se preciso salvá-lo no banco de dados, caso ele seja alterado.O problema surgiu porque o objeto original é uma referência... portanto, alterar someValue também alterou o objeto em cache referenciado, portanto, ele nunca seria salvo no banco de dados.Eu consertei clonando o objeto da versão em cache, cortando a referência e permitindo-me comparar o novo objeto com o armazenado em cache.
Minha pergunta é: existe uma maneira melhor de fazer isso, algum padrão que você possa recomendar? Não posso ser a única pessoa que já fez isso antes :)
Solução
O rastreamento sujo é a maneira normal de lidar com isso, eu acho.Algo como:
class MyObject {
public string SomeValue {
get { return _someValue; }
set {
if (value != SomeValue) {
IsDirty = true;
_someValue = value;
}
}
public bool IsDirty {
get;
private set;
}
void SaveToDatabase() {
base.SaveToDatabase();
IsDirty = false;
}
}
myoldObject = new MyObject { someValue = "old value" };
cache.Insert("myObjectKey", myoldObject);
myNewObject = cache.Get("myObjectKey");
myNewObject.someValue = "new value";
if(myNewObject.IsDirty)
myNewObject.SaveToDatabase();
Outras dicas
Já fiz coisas semelhantes, mas também contornei isso clonando.A diferença é que mandei o cache fazer a clonagem.Quando você coloca um objeto no cache, o cache clonará o objeto primeiro e armazenará a versão clonada (para que você possa alterar o objeto original sem envenenar o cache).Quando você obtém um objeto do cache, o cache retorna um clone do objeto em vez do objeto armazenado (novamente para que o chamador possa alterar o objeto sem afetar o objeto armazenado em cache/canônico).
Acho que isso é perfeitamente aceitável, desde que os dados que você está armazenando/enganando sejam pequenos.
Uma pequena melhoria na resposta do Marks ao usar o linq:
Ao usar o Linq, a busca de entidades do banco de dados marcará cada objeto como IsDirty.Fiz uma solução alternativa para isso, não definindo IsDirty quando o valor não está definido;para esta instância:quando nulo.Para ints, coloquei o valor original em -1 e verifiquei isso.Isso não funcionará, entretanto, se o valor salvo for igual ao valor não inicializado (nulo no meu exemplo).
private string _name;
[Column]
public string Name
{
get { return _name; }
set
{
if (value != _name)
{
if (_name != null)
{
IsDirty = true;
}
_name = value;
}
}
}
Provavelmente poderia ser melhorado configurando IsDirty após a inicialização de alguma forma.