2010-09-17 15 views
6

Edycja - Czyszczenie pytanie lepiej odzwierciedlać rzeczywisty problem:SQLMetal wielu kluczy obcych, wskazując na jeden stół Emisji

Używam SQLMetal do generowania klas z naszej bazy danych db SQL Server. Ostatnio musiałem dodać tabelę zawierającą wiele kluczy obcych wskazujących na tę samą tabelę. Korzystanie LINQPad się bawić z nowymi tabelami, byłem w stanie uzyskać dostęp do właściwości zarówno dla kluczy obcych tak:

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

... tak jakbym się tego spodziewał. Problem polega na tym, klas generowanych przez SQLMetal produkować te właściwości:

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

Teraz mógł po prostu przetworzy wygenerowane klasy, więc FKBTableNameGoesHere będzie FK_B, ale wygenerowane pliki są często zmieniane przez różnych członków zespołu, więc to byłby ogromny pai n. Czy jest to łatwe?

Z góry dziękuję.

Edycja 2 Tak, myślałem, że rozwiązaniem byłoby po prostu utworzyć częściowej klasy, który miał właściwość o nazwie co chciałem go mieć, i niech punkt getter/setter do słabo nazwie mienia. To działało, by wybrać, ale nie używać go tam, gdzie klauzule i tym podobne. KAŻDY ma rozwiązanie?

Odpowiedz

4

Tak więc, moim rozwiązaniem było dodanie kolejnej częściowej klasy i dodanie właściwości get/set wskazującej na dziwnie nazwaną właściwość FKBTableNameGoesHere. W ten sposób nie musimy ciągle modyfikować wygenerowanych klas. Niezupełnie rozwiązuje problem, ale powinno wyjaśnić programistom, co ta właściwość oznacza. Ktoś widzi potencjalne problemy z tym rozwiązaniem?

Edycja - Tak więc, najwyraźniej działa to tylko w przypadku wybierania danych, a nie filtrowania na podstawie tego. Nie tak łatwe, jak się spodziewałem. Czy ktoś ma jakieś inne sugestie?

Edycja 2 - Jezu, pomyślałem, że to będzie dość powszechny problem, ale nie sądzę. W każdym razie okazuje się, że byłem na dobrej drodze. Znalazłem ten post:

Multiple foreign keys to the same table

który dał mi pomysł, że nie mogę po prostu połączyć bezpośrednio do getter/setter dla innej posesji, ponieważ tam pewnie dużo więcej niż to dzieje się za kulisami. To rozwiązanie dla facetów nie było dokładnie odpowiedzią, ale wysłało mnie w tym kierunku.Dodawanie atrybutów skojarzenie to co w końcu zrobił to:

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

zamiar opuścić nagrodę otwarte dla każdego, kto może jeszcze wymyślić czystszego rozwiązania.

+0

Można to faktycznie zrobić krok dalej i użycie _OriginalPoorlyNamedStorageVariable można całkowicie pominąć. Nie jestem jednak pewien, czy dodanie własnej zmiennej pamięci w konstruktorze nie spowoduje innych komplikacji, ale prawdopodobnie wkrótce się dowiem ... – jahu

0

Ok, zaproponuję nową odpowiedź (trochę za późno, przepraszam), która zadziała, nawet jeśli zmieni się nazwa stowarzyszenia.

Ta metoda wyszuka właściwości powiązania głównej encji, a następnie wyszuka wartość w tabeli głównej. Wyobraź sobie:

Tabela: Orders, do której odwołuje się tabela Customers przez Orders.CustomerID jest równa Customers.Id. Tak więc przekazujemy informacje Meta głównego stołu, pole CustomerID (które jest polem odniesienia) i pole Name (które jest wartością, którą chcemy).

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

i wezwanie:

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

Problemem jest to, że działa tylko z kluczy obcych z tylko jednej dziedzinie odwołania. Ale zmiana na wiele kluczy jest dość banalna.

Jest to metoda uzyskania wartości pola tabeli wzorcowej, którą można zmienić, aby zwrócić cały obiekt.

PS: Myślę, że popełniam błędy w związku z moim angielskim, jest to dla mnie dość trudne.

0

To pytanie pojawiło się dawno temu, ale właśnie wpadłem na ten problem. Używam DBML i rozwiązałem ten problem, edytując relacje. Po rozwinięciu ParentProperty można ustawić nazwę właściwości, zmieniając właściwość Name.

Oto XML z dbml (atrybut państw zmienione):

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

Oto wariant wersji Ocelot20 za:

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

Z tym, że nie trzeba nawet znać Oryginalna nazwa magazynu, jednak nie jestem pewien, czy seter będzie działał (użyłem tylko gettera, ale działało jak czar). Możesz nawet spakować definicję relacji do klasy bazowej i uczynić ją dziedziczącą (jeśli musisz ponownie użyć relacji w wielu modelach \ kontekstach powinno to ułatwić), ale w tym przypadku musisz zdefiniować OnCreated() częściowe jak te nie mogą być dziedziczone w C# (see this).

Powiązane problemy