如何告诉EF不要先在代码中更新引用类型属性

本文关键字:代码 更新 引用类型 属性 何告诉 EF | 更新日期: 2023-09-27 18:06:00

让我们假设我有这两个模型

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以纯文本形式存储在数据库中,所以我使用了强制使用的加密方法;即使对于相同的原始字符串,它也会返回不同的加密字符串。我还设置了一些逻辑,分别在每次保存值和从数据库检索值时对字符串进行加密和解密。

那么,问题是什么?问题是,当我创建一个新的Person,给它分配一个电话并保存它时,EF将尝试也保存电话,并且考虑到电话上的IMEI字符串已经改变,它将在DB上创建一个新条目。

是否有一种方法可以告诉EF一个给定的对象不需要保存?这样我就能告诉它保存Person和它与Phone的关系,而不是手机本身?或者可能是一种丢弃对象中的更改以便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();
    }
}

我也更新了上面的原始定义。注:

  • Person中的Encrypt方法就在那里,因为当我保存Person时,它的附加电话也被保存,这是我试图避免的。
  • Encryptor类中我有扩展方法来加密列表中的所有对象,这是一个基本的迭代和加密。
  • 我实际上有更多的类和继承,为了这个问题,我简化了它。

如何告诉EF不要先在代码中更新引用类型属性

在保存实体之前,将其导航属性设置为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 h1> 果你想更新你的导航属性以及添加父实体,只需在保存之前将它们附加到上下文。
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。

SaveChanges()之前试试

db.Entry(person.Purchase).State = EntityState.Unchanged;

据我所知,你是这样的:

var phone = dbContext.Set<Phone>().First(o=>ID == id);
phone.IMEI = Encrypt(phone.IMEI);

因此,EF认为,电话被改变了。也许你应该把逻辑放到Phone类中:

class Phone
{
    public int ID { get; set; }
    private string _imei;
    public string IMEI 
    { 
        get { return Decrypt(_imei);}
        private set { _imei = Encrypt(value); }
    }
}

当EF从DB获取实体时,它会一次将IMEI值设置为加密,而不会处于更改状态。如果你给出一些从db获取电话的代码,会更好理解。

在这种情况下,您可以在Phone中创建一个附加属性,标记为NotMapped属性。

[NotMapped]
public string IMEIRaw { get; set; }