在MVC中更新Model-First Entity Framework POCO實體外鍵的方法


EF4.1支持了純粹的POCO實體,對編寫Persistence-Ignorant 的程序很有幫助。EF4.1還支持Code First的開發方式,但個人感覺利用Code First在處理較為復雜的關聯的時候還是力不從心,Model First是更加合適的方式。在MVC應用程序中,由於無法長久的保留DbContext,在更新一個實體的時候,通常的場景是這樣的:

ActionResult Edit(Entity entity)

{

//Init a context

// code to update entity

}

在這種情況下,EF自帶的ChangeTracker都起不到任何作用。假如我們有如下模型:

image

EF會生成如下的兩個實體類:

public partial class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual Address Address { get; set; }
}
public partial class Address
{
    public Address()
    {
        this.Person = new HashSet<Person>();
    }

    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Person> Person { get; set; }
}

看起來很完美,但是我們在更新Person的時候會遇到麻煩,例如:

static void Main(string[] args)
{
    var p = new Person { Id = 1, Name = "hello2", Address = new Address { Id = 2 } };
    Update(p);
}

static  bool Update(Person p)
{
    using (EFContainer con = new EFContainer())
    {
        con.Entry<Person>(p).State = System.Data.EntityState.Modified;
        con.SaveChanges();
    }
    return true;
}

 

運行的結果是:

image

Name屬性被修改了,但是外鍵沒有被修改。

改成這樣:

static  bool Update(Person p)
{
     using (EFContainer con = new EFContainer())
     {
          p.Address = con.AddressSet.Find(p.Address.Id);
          con.Entry<Person>(p).State = System.Data.EntityState.Modified;
          con.SaveChanges();
     }
     return true;
 }

結果還是一樣。EF的行為被設計成這樣很令人費解。所幸還有一種方法可以解決這個問題,就是顯式的在Person類中添加Address的外鍵。具體方法是,在EDMX設計器中,給Person類添加一個Scalar Propery,AddressID,在Table Mapping中,將其設置為AddressSet的ID,如下圖:

image

最后,雙擊表示關聯的線條,彈出一個外鍵約束框,如下設置:

image

設置完成以后,就可以如下使用:

static void Main(string[] args)
{         
    var p = new Person { Id=1, Name = "modified", AddressID=2 };
    Update(p);
}

static  bool Update(Person p)
{
    using (EFContainer con = new EFContainer())
    {
        con.Entry<Person>(p).State = System.Data.EntityState.Modified;
        con.SaveChanges();
    }
    return true;
}

一切正常。

純粹從設計的角度來說,在實體類中暴露外鍵——一個在關系數據庫中存在的概念並不是一個很好的設計方法,但是,目前似乎僅能通過這種方法使得EF能夠正確處理外鍵的更新,並且,在某些情況下,暴露外鍵也可以得到一些方便,暫且就這樣吧。

在新增實體的時候,如果不暴露外鍵,也會有種種問題,例如:

static void Main(string[] args)
{
    var p = new Person { Name = "hello5", Address = new Address { Id = 1, Name = "China" } };
    Create(p);
}

static bool Create(Person p)
{
    using (EFContainer con = new EFContainer())
    {
        con.PersonSet.Add(p);
        con.SaveChanges();
    }
    return true;
}

這代碼可以正確運行,但是結果並不是期望的新建一個名字為hello5的Person,其Address為ID=1的Address,事實上,EF會忽略掉Address中的Id=1,新增一個名字為China的Address,再將這個新增的Address的Id和這個Person關聯起來。這樣的行為也很費解,個人認為在顯式指定Address的主鍵的時候就不應該再去試圖新建Address實體,而應該直接關聯,反之,當沒有指定Address的主鍵的時候,應該新建一個。

不管如何,如果有了顯式的外鍵,那么,在需要關聯到已有的對象的時候,就直接使用AddressID來設置,否則就使用new Address來設置。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM