2010-09-17 16 views
6

Modifica - pulizia domanda per riflettere meglio il problema effettivo:SQLMetal più chiavi esterne che indicano un Tavolo Problema

sto usando SQLMetal per generare classi di database del nostro db SQL Server. Di recente, ho avuto bisogno di aggiungere una tabella con più chiavi esterne che puntavano alla stessa tabella. Utilizzando LINQPad di giocare con le nuove tabelle, sono stato in grado di accedere alle proprietà per entrambe le chiavi esterne in questo modo:

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

... che è proprio come me lo aspetterei. Il problema è che le classi generate da SQLMetal producono queste proprietà:

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

Ora ho potuto basta semplicemente fondere le classi generate in modo che FKBTableNameGoesHere sia FK_B, ma i file generati vengono cambiati molto spesso dai diversi membri del team, quindi sarebbe un enorme pai n. C'è una soluzione facile per questo?

Grazie in anticipo.

Edit 2 Così, ho pensato che la soluzione sarebbe quella di creare solo una classe parziale che aveva una proprietà denominata quello che volevo che fosse, e lasciare che il punto di getter/setter per la proprietà infelicemente chiamato. Questo ha funzionato per selezionare, ma non per utilizzarlo in clausole e simili. CHIUNQUE ha una soluzione ??

risposta

4

Quindi, la mia soluzione era quella di aggiungere solo un'altra classe parziale e aggiungere una proprietà con get/set che puntava alla strana proprietà denominata FKBTableNameGoesHere. In questo modo non dobbiamo continuare a modificare le classi generate. Non risolve esattamente il problema, ma dovrebbe chiarire agli sviluppatori che cosa significa la proprietà. Qualcuno vede potenziali problemi con quella soluzione?

Modifica - Quindi, apparentemente questo funziona solo per selezionare i dati e non filtrarli in base ad essi. Non è una soluzione facile come speravo. Qualcuno ha altri suggerimenti?

Modifica 2 - Jeeze, ho pensato che sarebbe stato un problema piuttosto comune, ma immagino di no. Comunque, risulta che ero sulla strada giusta con questo. Ho trovato questo post:

Multiple foreign keys to the same table

che mi ha dato l'idea che non potevo semplicemente collegarsi direttamente al getter/setter per un altro immobile dato che c'è probabilmente molto più di quello che succede dietro le quinte. Questa soluzione per ragazzi non era esattamente la risposta, ma mi ha mandato nella giusta direzione.Aggiunta di attributi l'associazione è ciò che alla fine lo ha fatto:

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

intenzione di lasciare la taglia aperto per tutti coloro che possono ancora trovare una soluzione più pulita.

+0

Questo può essere effettivamente fatto un passo avanti e l'uso di _OriginalPoorlyNamedStorageVariable può essere omesso interamente. Tuttavia non sono sicuro che se non includi la tua variabile di memoria nel costruttore non causerà altre complicazioni, ma probabilmente lo scoprirò molto presto ... – jahu

0

Ok, proporrò una nuova risposta (un po 'in ritardo, mi dispiace) che funzionerà anche se il nome dell'associazione cambia.

Questo metodo cercherà la proprietà di associazione dell'entità principale e quindi cercherà il valore nella tabella principale. Immaginate che:

Tabella: Orders riferimento con tavolo da CustomersOrders.CustomerID uguale Customers.Id. Quindi passiamo le informazioni Meta della tabella principale, il campo CustomerID (che è il campo di riferimento) e il campo Name (che è il valore che vogliamo).

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

E la chiamata:

 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 */); 

Il problema è che funziona solo con chiavi esterne con un solo campo di riferimento. Ma passare a chiavi multiple è piuttosto banale.

Questo è un metodo per ottenere un valore di un campo della tabella principale, è possibile modificare per restituire l'intero oggetto.

PS: Penso di fare qualche errore sul mio inglese, è abbastanza difficile per me.

0

Questa domanda è arrivata molto tempo fa, ma mi sono imbattuto in questo problema. Sto usando un DBML e ho risolto questo problema modificando le relazioni. Se si espande ParentProperty, è possibile impostare il nome della proprietà modificando la proprietà Name.

Ecco il XML dal DBML (l'attributo membro cambiato):

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

Qui è una variante della versione di 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 questo non è nemmeno necessario conoscere il nome di archiviazione originale, tuttavia non sono sicuro che il setter funzionerà (ho usato solo getter, ma ha funzionato come un incantesimo). Puoi persino impacchettare questa definizione di relazione in una classe base e renderla ereditaria (dovresti riutilizzare la relazione tra più modelli \ contesti questo dovrebbe renderlo più facile), ma il problema è che dovrai definire OnCreated() all'interno di ogni parziali come quelli non possono essere ereditati in C# (see this).

Problemi correlati