zoukankan      html  css  js  c++  java
  • 在MVC中更新ModelFirst 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来设置。

  • 相关阅读:
    zoj 1239 Hanoi Tower Troubles Again!
    zoj 1221 Risk
    uva 10192 Vacation
    uva 10066 The Twin Towers
    uva 531 Compromise
    uva 103 Stacking Boxes
    稳定婚姻模型
    Ants UVA
    Golden Tiger Claw UVA
    关于upper、lower bound 的探讨
  • 原文地址:https://www.cnblogs.com/yinzixin/p/2545605.html
Copyright © 2011-2022 走看看