C# 如何告诉EF不要先在代码中更新引用类型属性?
假设我有这两个模型C# 如何告诉EF不要先在代码中更新引用类型属性?,c#,entity-framework,encryption,ef-code-first,C#,Entity Framework,Encryption,Ef 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
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以明文形式存储在数据库中,所以我使用了我被迫使用的加密方法;即使对于相同的原始字符串,也会返回不同的加密字符串。我还有一些逻辑,可以在每次分别将值保存到数据库和从数据库检索时对字符串进行加密和解密
那么,有什么问题吗?
问题是,当我创建一个新的人
,为其分配一个电话并保存时,EF将尝试同时保存电话,并且,鉴于电话上的IMEI字符串已更改,它将在DB上创建一个新条目
有没有一种方法可以告诉EF给定的对象不需要保存?这样我就可以告诉它去救那个人和它和手机的关系,而不是手机本身?或者是一种放弃对象中的更改以便EF忽略它的方法
更新1
我有一些助手类来简化控制器中的逻辑,如下所示:
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();
}
}
公共类助手
{
受保护的CustomDBContext上下文{get;}=new CustomDBContext();
公共列表GetPhones()
{
返回Context.Phones.Decrypt();
}
公共作废保存(列出电话)
{
phones.Encrypt();
Context.Phones.AddRange(电话);
SaveChanges();
}
公共列表GetPeople()
{
返回Context.Persons.Decrypt();
}
公共作废保存(列出人员)
{
people.Encrypt();
Context.Persons.AddRange(人);
SaveChanges();
}
}
我还更新了上述原始定义。注:
中的Person
方法就在那里,因为当我保存Person时,它附带的电话也会保存下来,这是我试图避免的Encrypt
- 在
类中,我有扩展方法来加密列表中的所有对象,这是一个基本的迭代和加密Encryptor
- 实际上,我有更多的类和继承以及整个shebang,为了这个问题,我简化了它
据我所知,你是这样想的:
var phone = dbContext.Set<Phone>().First(o=>ID == id);
phone.IMEI = Encrypt(phone.IMEI);
当EF从DB中获取实体时,它将一度将IMEI值设置为加密,并且不会处于更改状态。
如果您提供了一些从db获取电话的代码,那么更好地理解它。在保存实体之前,请将其导航属性设置为null。这将保留外键,但删除与导航属性关联的实体引用。在图形中搜索要添加的实体时,它将在导航属性中找不到任何内容
public void Save(List<Person> people)
{
people.Encrypt();
foreach(var person in people){
person.Purchase = null;
}
Context.Person.AddRange(people);
Context.SaveChanges();
}
公共作废保存(列出人员)
{
people.Encrypt();
foreach(人与人之间的变量){
person.Purchase=null;
}
Context.Person.AddRange(人);
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();
}
}
class电话
{
公共int ID{get;set;}
公共字符串IMEI{get;set;}
公共虚拟ICollection人员{get;set;}
公共void Encrypt()
{
加密器。加密(IMEI);
}
}
班主任
{
公共int ID{get;set;}
公共int PhoneId{get;set;}
公共虚拟电话购买{get;set;}
公共void Encrypt()
{
Purchase.Encrypt();
}
}
您可以在文档中找到有关导航属性的更多信息:
更新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();
}
公共作废保存(列出人员)
{
people.Encrypt();
foreach(人与人之间的变量){
Context.Phones.Attach(person.Purchase)
}
Context.Person.AddRange(人);
SaveChanges();
}
这将允许您在每次保存时更新数据库中加密的IMEI。在这种情况下,您可以在
手机中创建一个附加属性,并用属性标记
[NotMapped]
public string IMEIRaw { get; set; }
您将在该属性中存储解密的值。为什么IMEI发生了更改?因为它被解密了?如果是这种情况,您可以创建一个只读属性DecryptedIMEI
,该属性返回解密的IMEI,并保持IMEI
属性不变(加密)。您可以在decryptedimi
属性上设置[Ignore]属性,这样EF就不会对其进行任何处理(尽管我认为将其setter标记为private应该足够了)@Leo由于我完全控制代码,我可以通过多种方法解决此问题,当然您的方法就是其中之一,我还可以将加密的值存储在某个地方,并且只有在值不存在时才重新加密。我只是想一定有办法改变EF的行为。我不明白为什么。如果您将加密的IMEI分配给其他人(我知道您是这样做的),则会更改该IMEI。2.如果某个实体的属性发生更改,为什么EF会将该实体标记为添加的
。1每次保存手机时,IMEI都会更改,因为我会重新加密该值,而该方法会返回不同的加密字符串。2 IMEI是唯一索引的一部分,所以我想这就是为什么第2部分没有意义。EF不知道db索引(即使它可以通过迁移创建它们)。您应该显示获取和保存实体的代码(基本部分)。我已经尝试过了,但它不起作用。这还带来了另一个问题,我需要确保将图中的所有对象更改为未更改,这与必须加密图中的所有对象没有太大区别;在应用程序中使用时,以这种方式使用它不会解密,请记住,我只需要在加密时使用它
public void Save(List<Person> people)
{
people.Encrypt();
foreach(var person in people){
person.Purchase = null;
}
Context.Person.AddRange(people);
Context.SaveChanges();
}
[NotMapped]
public string IMEIRaw { get; set; }