2010-09-17 15 views
6

Editar - Limpieza de pregunta para reflejar mejor el problema real:SQLMetal claves externas múltiples señalando una tabla Edición

estoy usando SQLMetal para generar clases de bases de datos de nuestro db SQL Server. Recientemente, necesité agregar una tabla que tenía varias claves foráneas apuntando a la misma tabla. Usando LINQPad a jugar con las nuevas tablas, yo era capaz de acceder a las propiedades de ambas claves externas de este modo:

  • record.FK_AId
  • record.FK_BId
  • record.FKA
  • record.FKB

... que es lo que se puede esperar la misma. El problema es que las clases generadas por SQLMetal producen estas propiedades:

  • record.FK_AId
  • record.FK_BId
  • record.FKA
  • record.FKBTableNameGoesHere

Ahora podía simplemente cange las clases generadas por lo que FKBTableNameGoesHere sería FK_B, pero los archivos generados son cambiados muy a menudo por diferentes miembros del equipo, por lo que sería un gran pai norte. ¿Hay alguna manera sencilla de reparar esto?

Gracias de antemano.

Editar 2 Por lo tanto, pensé que la solución sería simplemente crear una clase parcial que tenía una propiedad denominada lo que yo quería que fuera, y dejar que el punto de captador/definidor de la propiedad mal llamada. Eso funcionó para seleccionar, pero no usarlo en cláusulas where y tal. ¿¿Alguien tiene una solución??

Respuesta

4

lo tanto, mi solución era simplemente añada otra clase parcial y añadir una propiedad con el get/set apuntado a la propiedad FKBTableNameGoesHere extraño nombre. De esta forma no tenemos que seguir modificando las clases generadas. No es exactamente la solución del problema, pero debe aclarar a los desarrolladores lo que significa la propiedad. ¿Alguien ve algún problema potencial con esa solución?

Editar - Por lo tanto, aparentemente esto solo funciona para seleccionar datos y no para filtrarlos. No es tan fácil de arreglar como esperaba. Alguien tiene alguna otra sugerencia?

Editar 2 - Jeeze, pensó que este sería un problema común, pero supongo que no. De todos modos, resulta que estaba en el camino correcto con esto. He encontrado este post:

Multiple foreign keys to the same table

que me dio la idea de que yo no podía ir directamente al captador/definidor de otra propiedad ya que es probable que haya mucho más de lo que sucede detrás de las escenas. La solución de este tipo no fue exactamente la respuesta, pero me envió en la dirección correcta.La adición de los atributos de la asociación es lo que finalmente lo hizo:

public partial class ProblemClass 
{ 
    [Association(Name = "FK__SomeLinkHere", Storage = "_OriginalPoorlyNamedStorageVariable", ThisKey = "FK_1_Id", OtherKey = "Id", IsForeignKey = true)] 
    public FKType MyNewBetterName 
    { 
     get 
     { 
      return this._OriginalPoorlyNamedStorageVariable.Entity; 
     } 

     set 
     { 
      this.OriginalPoorlyNamedStorageVariable = value; 
     } 
    } 
} 

va a dejar la generosidad abierta para cualquier persona que todavía puede llegar a una solución más limpia.

+0

Esto se puede dar un paso más y se puede omitir por completo _OriginalPoorlyNamedStorageVariable. Sin embargo, no estoy seguro si no incluir su propia variable de almacenamiento en el constructor no causará otras complicaciones, pero probablemente lo descubra muy pronto ... – jahu

0

Ok, propondré una nueva respuesta (un poco tarde, lo siento) que funcionará incluso si el nombre de la asociación cambia.

Este método buscará la propiedad de asociación de la entidad principal y luego buscará el valor en la tabla maestra. Imaginemos que:

Tabla: Orders referenciado con mesa Customers por Orders.CustomerID es igual a Customers.Id. Pasamos la información del Meta de la tabla principal, el campo CustomerID (que es el campo al que se hace referencia) y el campo Name (que es el valor que queremos).

/// <summary> 
/// Gets the value of "referencedValueFieldName" of an associated table of the "fieldName" in the "mainTable". 
/// This is equivalent of doing the next LINQ query: 
/// var qryForeignValue = 
///  from mt in modelContext.mainTable 
///  join at in modelContext.associatedTable 
///  on mt.fieldName equals at.associatedField 
///  select new { Value = at.referencedValueField } 
/// </summary> 
/// <param name="mainTable">Metadata of the table of the fieldName's field</param> 
/// <param name="fieldName">Name of the field of the foreign key</param> 
/// <param name="referencedValueFieldName">Which field of the foreign table do you the value want</param> 
/// <returns>The value of the referenced table</returns> 
/// <remarks>This method only works with foreign keys of one field.</remarks> 
private Object GetForeignValue(MetaTable mainTable, string fieldName, string referencedValueFieldName) { 
    Object resultValue = null; 
    foreach (MetaDataMember member in mainTable.RowType.DataMembers) { 
    if ((member.IsAssociation) && (member.Association.IsForeignKey)) { 
     if (member.Association.ThisKey[0].Name == fieldName) { 
     Type associationType = fPointOfSaleData.GetType(); 
     PropertyInfo associationInfo = associationType.GetProperty(member.Name); 
     if (associationInfo == null) 
      throw new Exception("Association property not found on member"); 

     Object associationValue = associationType.InvokeMember(associationInfo.Name, BindingFlags.GetProperty, null, fPointOfSaleData, null); 

     if (associationValue != null) { 
      Type foreignType = associationValue.GetType(); 
      PropertyInfo foreignInfo = foreignType.GetProperty(referencedValueFieldName); 
      if (foreignInfo == null) 
      throw new Exception("Foreign property not found on assiciation member"); 

      resultValue = foreignType.InvokeMember(foreignInfo.Name, BindingFlags.GetProperty, null, associationValue, null); 
     } 

     break; 
     } 
    } 
    } 
    return resultValue; 
} 

Y la llamada:

 AttributeMappingSource mapping = new System.Data.Linq.Mapping.AttributeMappingSource(); 
     MetaModel model = mapping.GetModel(typeof(WhateverClassesDataContext)); 
     MetaTable table = model.GetTable(typeof(Order)); 
     Object value = GetForeignValue(table, "CustomerId" /* In the main table */, "Name" /* In the master table */); 

El problema es que sólo funciona con claves externas con sólo un campo de referencia. Pero, cambiar a varias claves es bastante trivial.

Este es un método para obtener un valor de un campo de la tabla maestra, puede cambiar para devolver el objeto completo.

PD: Creo que cometo algunos errores sobre mi inglés, es bastante difícil para mí.

0

Esta pregunta llegó hace mucho tiempo, pero me encontré con este problema. Estoy usando un DBML y resolví este problema editando las relaciones. Si expande ParentProperty, puede establecer el nombre de la propiedad cambiando la propiedad Name.

Aquí está el XML desde el DBML (el atributo de miembro cambiado):

<Association Name="State_StateAction1" Member="DestinationState" ThisKey="DestinationStateId" OtherKey="Id" Type="State" IsForeignKey="true" /> 
0

Aquí es una variante de la versión de Ocelot20:

public partial class ProblemClass 
{ 
    partial void OnCreated() 
    { 
     this._MyNewBetterName = default(EntityRef<FKType>); 
    } 
    protected EntityRef<FKType> _MyNewBetterName; 

    [Association(Name = "FK__SomeLinkHere", Storage = "_MyNewBetterName", ThisKey = "FK_1_Id", OtherKey = "Id", IsForeignKey = true)] 
    public EntityRef<FKType> MyNewBetterName 
    { 
     get 
     { 
      return this._MyNewBetterName.Entity; 
     } 

     set 
     { 
      this.MyNewBetterName = value; 
     } 
    } 
} 

Con esto, usted ni siquiera necesita saber la nombre de almacenamiento original, sin embargo, no estoy seguro de si el colocador funcionará (solo usé getter, pero funcionó como un amuleto). Incluso puede empaquetar esta definición de relación en una clase base y hacer que la parcial la herede (si necesita reutilizar la relación en varios contextos de modelos, esto debería hacerlo más fácil), pero la clave es que tendrá que definir OnCreated() dentro de cada una parcial, ya que no se pueden heredar en C# (see this).

Cuestiones relacionadas