代码之家  ›  专栏  ›  技术社区  ›  Adrian Grigore

实体框架:设置新对象引用时自动更新外键

  •  8
  • Adrian Grigore  · 技术社区  · 15 年前

    我正在将一个现有的应用程序从Linq移植到SQL实体框架4(默认代码生成)。

    我注意到两者之间的一个区别是,重置对象引用时外键属性不会更新。现在我需要决定如何处理这个问题。

    例如,假设您有两个实体类型:公司和员工。一家公司有很多员工。

    在Linq To SQL中,设置公司也会设置公司id:

    var company=new Company(ID=1);
    var employee=new Employee();
    Debug.Assert(employee.CompanyID==0);
    employee.Company=company;
    Debug.Assert(employee.CompanyID==1); //Works fine!
    

    var company=new Company(ID=1);
    var employee=new Employee();
    Debug.Assert(employee.CompanyID==0);
    employee.Company=company;
    Debug.Assert(employee.CompanyID==1); //Throws, since CompanyID was not updated!
    

    如何使EF的行为与LinqToSQL相同?我查看了默认的代码生成T4模板,但是我不知道如何进行必要的更改。看起来单行程序应该可以做到这一点,但是我不知道如何获得给定引用的ID属性。

    2 回复  |  直到 14 年前
        1
  •  4
  •   Dynami Le Savard    15 年前

    从缺省T4模板中可以看到,实体的外键属性没有直接链接到与该键关联的实体引用。

    关于从Linq到SQL再到EF4的迁移,有两种方法可以解决您的问题。其中之一就是注册 AssociationChanged

    // Extends Employee entity
    public partial class Employee
    {
        private void CompanyChanged(Object sender, CollectionChangeEventArgs e)
        {
            // Apply reactive changes; aka set CompanyID
            // here
        }
    
        // Create a default constructor that registers your event handler
        public Employee()
        {
            this.CompanyReference.AssociationChanged += CompanyChanged;
        }
    }
    

    就个人而言,如果您想限制维护这种逻辑所需的维护,我建议您更改T4模板(您自己更改或找到一个模板),以便它设置 CompanyId 什么时候 Company

    Gil Fink 写了一个很好的介绍T4模板与EF4,你可以看看 Scott Hanselman 包装了一堆有用的链接和资源来使用T4模板。

    最后一点,除非我弄错了,否则从EF3.5到EF3.4,直接作为实体的属性访问外键是一个新事物( Employee.Company.CompanyID ). 我相信这个特性是在EF4中添加的,这样您就不必加载关联(使用“include”)以便在从数据存储中选择时获得外键。

    也许英孚的做法是,如果你得到了协会,首先要通过协会获得身份证。但那只是猜测,因为我没有任何引述来支持它。

    【编辑2010-06-16】 在快速通读和分析edmxxml元素之后,我发现了一个名为ReferentialConstraint的元素,它似乎包含一个特定FKèu关系的外键字段。

    下面是要在默认T4 edmx模板中修改的代码段,请参阅“编写导航属性”部分( Template_RegionNavigationProperties

    <#=code.SpaceAfter(NewModifier(navProperty))#><#=Accessibility.ForProperty(navProperty)#> <#=MultiSchemaEscape(navProperty.ToEndMember.GetEntityType(), code)#> <#=code.Escape(navProperty)#>
        {
            <#=code.SpaceAfter(Accessibility.ForGetter(navProperty))#>get
            {
                return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<<#=MultiSchemaEscape(navProperty.ToEndMember.GetEntityType(), code)#>>("<#=navProperty.RelationshipType.FullName#>", "<#=navProperty.ToEndMember.Name#>").Value;
            }
            <#=code.SpaceAfter(Accessibility.ForSetter(navProperty))#>set
            {
                // edit begins here
                if(value != null)
                {
                    // Automatically sets the foreign key attributes according to linked entity
    
    <#
                AssociationType association = GetSourceSchemaTypes<AssociationType>().FirstOrDefault(_ => _.FullName == navProperty.RelationshipType.FullName);
                foreach(var cons in  association.ReferentialConstraints)
                {
                    foreach(var metadataProperty in cons.FromProperties)
                    {
    #>
                    this.<#=metadataProperty.Name#> = value.<#=metadataProperty.Name#>;
                    //this._<#=metadataProperty.Name#> = value._<#=metadataProperty.Name#>; // use private field to bypass the OnChanged events, property validation and the likes..
    
    <#
                    }
                }
    #>  
                }
                else
                {
                    // what usually happens in Linq-to-SQL when an association is set to null
                    // here
                }
                // edit ends here
    
                ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<<#=MultiSchemaEscape(navProperty.ToEndMember.GetEntityType(), code)#>>("<#=navProperty.RelationshipType.FullName#>", "<#=navProperty.ToEndMember.Name#>").Value = value;
            }
        }
    

    我粗略地测试了一下,但这是一个假设,有一些验证和这样的缺失。或许它可以给你一个提示,让你找到一个解决方案。

        2
  •  2
  •   codetuner    14 年前

    谢谢你的解决方案。我已经对它进行了增强(不再依赖于特定的命名约定),并包含在一个修复程序中,该修复程序还修复了实体框架模板的其他问题。

    Check here for my solution and fixed code generation template