如何告诉 EF 不要在 Code First 中更新引用类型 属性?
How to tell EF not to Update Reference Type Property in Code First?
假设我有这两个模型
class Phone
{
public int ID { get; set; }
public string IMEI { get; set;}
public void Encrypt()
{
Encryptor.Encrypt(IMEI);
}
}
class Person
{
public int ID { get; set; }
public Phone Purchase { get; set; }
public void Encrypt()
{
Purchase.Encrypt();
}
}
现在碰巧,为了安全起见,我不能将IMEI以明文形式存储在数据库中,所以我使用了被迫使用的加密方法; returns 不同的加密字符串,即使是相同的原始字符串。每次将值分别保存到数据库和从数据库检索时,我也有一些逻辑来加密和解密字符串。
所以,问题是什么?
问题是,当我创建一个新的 Person
时,为其分配一个 Phone 并保存它,EF 将尝试同时保存 Phone 并且假设 [= 上的 IMEI 字符串46=] 已更改,它将在数据库中创建一个新条目。
有没有一种方法可以告诉 EF 不需要保存给定的对象?这样我就可以告诉它保存 Person 及其与 Phone 的关系,而不是 phone 本身?或者可能是丢弃对象中的更改以便 EF 忽略它的方法?
更新 1
我有一些助手 classes 来简化控制器中的逻辑,如下所示:
public class Helpers
{
protected CustomDBContext Context { get; } = new CustomDBContext();
public List<Phone> GetPhones()
{
return Context.Phones.Decrypt();
}
public void Save(List<Phone> phones)
{
phones.Encrypt();
Context.Phones.AddRange(phones);
Context.SaveChanges();
}
public List<Person> GetPeople()
{
return Context.Persons.Decrypt();
}
public void Save(List<Person> people)
{
people.Encrypt();
Context.Persons.AddRange(people);
Context.SaveChanges();
}
}
我也更新了上面的原始定义。备注:
Person
中的 Encrypt
方法就在那里,因为当我保存 Person 时,其附加的 Phone 也被保存,这是我试图避免的。
- 在
Encryptor
class 我有扩展方法来加密列表中的所有对象,这是一个基本的迭代和加密。
- 我实际上有更多的classes和继承以及整个shebang,我为了这个问题的目的简化了它。
先试试这个 SaveChanges()
db.Entry(person.Purchase).State = EntityState.Unchanged;
据我了解,你是这样的:
var phone = dbContext.Set<Phone>().First(o=>ID == id);
phone.IMEI = Encrypt(phone.IMEI);
因此,EF 认为 phone 已更改。
也许你应该把逻辑放入 Phone class:
class Phone
{
public int ID { get; set; }
private string _imei;
public string IMEI
{
get { return Decrypt(_imei);}
private set { _imei = Encrypt(value); }
}
}
当EF从数据库中获取实体时,它会将IMEI值设置为加密一次,并且不会处于更改状态。
如果你给出一些从db获取phone的代码,会更好理解。
在保存实体之前,将其导航属性设置为 null。这将保留外键但删除与导航关联的实体引用 属性。在图表中搜索要添加的实体时,它将找不到任何东西代替导航 属性.
public void Save(List<Person> people)
{
people.Encrypt();
foreach(var person in people){
person.Purchase = null;
}
Context.Person.AddRange(people);
Context.SaveChanges();
}
更新
确保您的实体的导航属性也正确建模。
class Phone
{
public int ID { get; set; }
public string IMEI { get; set;}
public virtual ICollection<Person> People { get; set; }
public void Encrypt()
{
Encryptor.Encrypt(IMEI);
}
}
class Person
{
public int ID { get; set; }
public int PhoneId { get; set; }
public virtual Phone Purchase { get; set; }
public void Encrypt()
{
Purchase.Encrypt();
}
}
您可以在文档中找到有关导航属性的更多信息:https://msdn.microsoft.com/en-us/data/jj713564.aspx
更新 2
如果您要更新导航属性并添加父实体,只需在保存前将它们附加到上下文即可。
public void Save(List<Person> people)
{
people.Encrypt();
foreach(var person in people){
Context.Phones.Attach(person.Purchase)
}
Context.Person.AddRange(people);
Context.SaveChanges();
}
这将允许您在每次保存时更新数据库中的加密 IMEI。
在这种情况下,您可以在标有 NotMapped
属性的 Phone
中创建一个额外的 属性。
[NotMapped]
public string IMEIRaw { get; set; }
您将在此 属性 中存储解密值。
假设我有这两个模型
class Phone
{
public int ID { get; set; }
public string IMEI { get; set;}
public void Encrypt()
{
Encryptor.Encrypt(IMEI);
}
}
class Person
{
public int ID { get; set; }
public Phone Purchase { get; set; }
public void Encrypt()
{
Purchase.Encrypt();
}
}
现在碰巧,为了安全起见,我不能将IMEI以明文形式存储在数据库中,所以我使用了被迫使用的加密方法; returns 不同的加密字符串,即使是相同的原始字符串。每次将值分别保存到数据库和从数据库检索时,我也有一些逻辑来加密和解密字符串。
所以,问题是什么?
问题是,当我创建一个新的 Person
时,为其分配一个 Phone 并保存它,EF 将尝试同时保存 Phone 并且假设 [= 上的 IMEI 字符串46=] 已更改,它将在数据库中创建一个新条目。
有没有一种方法可以告诉 EF 不需要保存给定的对象?这样我就可以告诉它保存 Person 及其与 Phone 的关系,而不是 phone 本身?或者可能是丢弃对象中的更改以便 EF 忽略它的方法?
更新 1
我有一些助手 classes 来简化控制器中的逻辑,如下所示:
public class Helpers
{
protected CustomDBContext Context { get; } = new CustomDBContext();
public List<Phone> GetPhones()
{
return Context.Phones.Decrypt();
}
public void Save(List<Phone> phones)
{
phones.Encrypt();
Context.Phones.AddRange(phones);
Context.SaveChanges();
}
public List<Person> GetPeople()
{
return Context.Persons.Decrypt();
}
public void Save(List<Person> people)
{
people.Encrypt();
Context.Persons.AddRange(people);
Context.SaveChanges();
}
}
我也更新了上面的原始定义。备注:
Person
中的Encrypt
方法就在那里,因为当我保存 Person 时,其附加的 Phone 也被保存,这是我试图避免的。- 在
Encryptor
class 我有扩展方法来加密列表中的所有对象,这是一个基本的迭代和加密。 - 我实际上有更多的classes和继承以及整个shebang,我为了这个问题的目的简化了它。
先试试这个 SaveChanges()
db.Entry(person.Purchase).State = EntityState.Unchanged;
据我了解,你是这样的:
var phone = dbContext.Set<Phone>().First(o=>ID == id);
phone.IMEI = Encrypt(phone.IMEI);
因此,EF 认为 phone 已更改。 也许你应该把逻辑放入 Phone class:
class Phone
{
public int ID { get; set; }
private string _imei;
public string IMEI
{
get { return Decrypt(_imei);}
private set { _imei = Encrypt(value); }
}
}
当EF从数据库中获取实体时,它会将IMEI值设置为加密一次,并且不会处于更改状态。 如果你给出一些从db获取phone的代码,会更好理解。
在保存实体之前,将其导航属性设置为 null。这将保留外键但删除与导航关联的实体引用 属性。在图表中搜索要添加的实体时,它将找不到任何东西代替导航 属性.
public void Save(List<Person> people)
{
people.Encrypt();
foreach(var person in people){
person.Purchase = null;
}
Context.Person.AddRange(people);
Context.SaveChanges();
}
更新
确保您的实体的导航属性也正确建模。
class Phone
{
public int ID { get; set; }
public string IMEI { get; set;}
public virtual ICollection<Person> People { get; set; }
public void Encrypt()
{
Encryptor.Encrypt(IMEI);
}
}
class Person
{
public int ID { get; set; }
public int PhoneId { get; set; }
public virtual Phone Purchase { get; set; }
public void Encrypt()
{
Purchase.Encrypt();
}
}
您可以在文档中找到有关导航属性的更多信息:https://msdn.microsoft.com/en-us/data/jj713564.aspx
更新 2
如果您要更新导航属性并添加父实体,只需在保存前将它们附加到上下文即可。
public void Save(List<Person> people)
{
people.Encrypt();
foreach(var person in people){
Context.Phones.Attach(person.Purchase)
}
Context.Person.AddRange(people);
Context.SaveChanges();
}
这将允许您在每次保存时更新数据库中的加密 IMEI。
在这种情况下,您可以在标有 NotMapped
属性的 Phone
中创建一个额外的 属性。
[NotMapped]
public string IMEIRaw { get; set; }
您将在此 属性 中存储解密值。