2010-06-11 38 views
8

Estoy portando una aplicación existente de Linq a SQL a Entity Framework 4 (generación de código predeterminada).Entity Framework: actualizar automáticamente la clave externa al establecer una nueva referencia de objeto

Una diferencia que noté entre los dos es que una propiedad de clave externa no se actualiza al restablecer la referencia del objeto. Ahora necesito decidir cómo lidiar con esto.

Por ejemplo, supongamos que tiene dos tipos de entidad, Compañía y Empleado. Una compañía tiene muchos empleados.

En LINQ to SQL, el establecimiento de la compañía también establece el identificador de empresa:

En Entity Framework (y sin necesidad de utilizar ninguna personalización de plantillas de código) Esto no funciona:

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! 

¿Cómo puedo hacer que EF se comporte de la misma manera que LinqToSQL? Eché un vistazo a la plantilla de generación de código predeterminada T4, pero no pude entender cómo hacer los cambios necesarios. Parece que un one-liner debería ser el truco, pero no pude averiguar cómo obtener la propiedad de ID para una referencia determinada.

+0

Si guardó los cambios con lo que usted describe, es el campo en el valor correcto una vez que seleccione su entidad de vuelta desde el almacén de datos? –

+0

Sí, por supuesto que sí. Pero tengo muchos códigos heredados que aún dependen del comportamiento de L2S. –

Respuesta

4

Según lo que puedo ver en la plantilla predeterminada de T4, las propiedades de clave externa de las entidades no están directamente vinculadas a la referencia de entidad asociada con la clave.

Hay parejas para abordar su problema con respecto a la migración de Linq a SQL a EF4. Una de ellas sería registrarse en el evento AssociationChanged de sus asociaciones para que actualice su campo automáticamente. En su contexto, un enfoque podría ser algo así como esto:

// 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; 
    } 
} 

Personalmente, si desea limitar el mantenimiento requerido para mantener este tipo de lógica, sugeriría cambiar la plantilla de T4 (ya sea cambiar usted mismo o encuentre uno) para que establezca el CompanyId cuando Company se modifique como se muestra anteriormente.

Gil Fink escribió una muy buena introducción a las plantillas de T4 con EF4, y puede buscar Scott Hanselman envuelto un buen grupo de enlaces útiles y recursos para trabajar con plantillas de T4.

En una última nota, a menos que esté equivocado, acceder a claves externas directamente como protenes de una entidad es algo nuevo de EF3.5 a 4. En 3.5, solo se podía acceder a través de la entidad asociada (Employee.Company.CompanyID) Creo que la función se agregó en EF4 para que no tenga que cargar asociaciones (usando "incluir") para obtener la clave externa cuando se selecciona desde el almacén de datos.

Quizás EF asuma esto, si tiene la asociación, vaya a través de la asociación para obtener la identificación, ante todo. Pero eso es solo especulación, ya que no obtuve comillas para respaldarlo.

[EDIT 2010-06-16]: Después de una lectura completa y análisis de los elementos edmx XML rápida, encontré uno llamado ReferentialConstraint que parece contener campos de clave externa a un FK_Relation specfic.

Aquí está el fragmento de código para modificar dentro de una plantilla de T4 edmx predeterminada, sección Escribir propiedades de navegación. (Template_RegionNavigationProperties), alrededor de la línea 388 de una plantilla no modificada.Trate de ignorar el formato horribles ...

<#=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; 
     } 
    } 

más o menos lo he probado, pero es un hecho que los theres una cierta validación y tales que falta. Tal vez podría darle un consejo para una solución independientemente.

+0

Gracias por su respuesta. Agregar un controlador de eventos para cada asociación funcionaría, pero sería una gran cantidad de trabajo (erróneo) en el modelo actual más una gran posibilidad de que me olvide de hacerlo más tarde. Entonces, el enfoque T4 realmente parece ser el camino a seguir. Como dije, ya miré la plantilla T4. Generalmente sé cómo funciona T4, ya tengo bastante experiencia con él, pero no pude encontrar la manera de obtener la propiedad de ID para una referencia dada de los metadatos de EF. Me di por vencido después de unas horas y comencé esta publicación ... –

+0

De hecho, parece que no hay ningún vínculo entre la propiedad FK y la referencia FK, ni siquiera un atributo personalizado. ¿Sigues algún tipo de convención de nombres para tus claves foráneas que es constante a través de tu DB relacional? –

+0

Por lo general, sí, entonces la reflexión probablemente funcionaría. Si no hay otra opción (esperaba que me perdiera algún enlace entre la referencia y el ID), trataré de utilizar este enfoque. –

Cuestiones relacionadas