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来设置。

  • 相关阅读:
    python模拟shell
    10.LIKE 操作符
    9.TOP 子句--mysql limit
    8.INSERT INTO 语句 UPDATE 语句
    7.ORDER BY 子句
    6.AND & OR 运算符
    5.WHERE 子句
    4.SELECT DISTINCT 语句
    3.SELECT 语句
    2.sql分类
  • 原文地址:https://www.cnblogs.com/yinzixin/p/2545605.html
Copyright © 2011-2022 走看看