2010-09-17 1 views
6

Редактировать - Очистка вопрос, чтобы лучше отражать реальную проблему:SqlMetal несколько внешних ключей, ссылающихся на одну таблицу Выпуск

Я использую SQLMetal для создания классов базы данных из нашей БД SQL Server. Недавно мне нужно было добавить таблицу с несколькими внешними ключами, указывающими на одну и ту же таблицу. Использование LINQPad играть с новыми таблицами, я был в состоянии получить доступ к свойствам как для внешних ключей, так как:

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

... который как раз и ожидал. Проблема заключается в том, что классы, порожденные SQLMetal производят эти свойства:

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

Теперь я мог просто сгенерируйте созданные классы таким образом, что FKBTableNameGoesHere будет FK_B, но сгенерированные файлы очень часто меняются разными членами команды, так что это будет огромный пай п. Есть ли легкое решение для этого?

Заранее спасибо.

Edit 2 Итак, я думал, что решение было бы просто создать частичный класс, который имел свойство с именем, что я хотел, чтобы это было, и пусть точка геттер/сеттер в плохо именованного свойства. Это работало на выбор, но не на его использование в тех случаях, когда клаузулы и т. Д. У любого есть решение?

ответ

4

Итак, мое решение состояло в том, чтобы просто добавить еще один частичный класс и добавить свойство с помощью метода get/set, указывающего на свойство с необычным именем FKBTableNameGoesHere. Таким образом, нам не нужно изменять модифицированные классы. Не совсем решение проблемы, но должно сделать более понятным для разработчиков то, что означает свойство. Кто-нибудь видит какие-либо потенциальные проблемы с этим решением?

Редактировать - Таким образом, очевидно, что это работает только для выбора данных, а не для фильтрации на его основе. Не так легко исправить, как я надеялся. У кого-нибудь есть другие предложения?

Редактировать 2 - Чиже, подумал, что это будет какой-то общей проблемой, но, я думаю, нет. Во всяком случае, оказывается, я был на правильном пути с этим. Я нашел этот пост:

Multiple foreign keys to the same table

Который дал мне идею, что я просто не мог связать непосредственно с геттер/сеттер для другого имущества, так как это, вероятно, намного больше, чем это происходит за кулисами. Решение этих парней было не совсем точным ответом, но он отправил меня в правильном направлении.Добавление атрибутов ассоциации это то, что в конце концов сделали это:

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

собирается покинуть Баунтите открытую для тех, кто еще может придумать более чистым раствором.

+0

Это можно сделать на шаг дальше, и использование _OriginalPoorlyNamedStorageVariable может быть полностью опущено. Однако я не уверен, что если не включить вашу собственную переменную хранилища в конструктор, это не вызовет каких-либо других осложнений, но я, вероятно, скоро узнаю ... – jahu

0

Хорошо, я предложу новый ответ (немного поздно, извините), который будет работать, даже если имя ассоциации изменяется.

Этот метод будет искать свойство ассоциации основного объекта и затем будет искать значение в главной таблице. Представьте себе, что:

Таблица: Orders ссылки с таблицей Customers по Orders.CustomerID равна Customers.Id. Таким образом, мы передаем метаинформацию основной таблицы, поле CustomerID (которое является ссылочным полем) и поле Name (это то значение, которое мы хотим).

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

И призыв:

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

Проблема заключается в том, что работает только с внешними ключами только с одной ссылочного поля. Но переход на несколько ключей довольно тривиален.

Это метод получения значения поля главной таблицы, который вы можете изменить, чтобы вернуть весь объект.

PS: Я думаю, что делаю некоторые ошибки в своем английском, это довольно сложно для меня.

0

Этот вопрос возник очень давно, но я столкнулся с этой проблемой. Я использую DBML, и я решил эту проблему, отредактировав отношения. Если вы расширите ParentProperty, вы можете установить имя свойства, изменив свойство Name.

Вот XML с DBML (атрибут членами изменен):

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

Вот вариант версии 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; 
     } 
    } 
} 

При этом вы даже не нужно знать оригинальное имя хранилища, однако я не уверен, что сеттер будет работать (я использовал только геттер, но он работал как шарм). Вы даже можете упаковать это определение отношения в базовый класс и сделать его частичным наследовать (если вам нужно повторно использовать отношение между несколькими моделями \ контексты, это должно облегчить его), но уловка, вам нужно будет определить OnCreated() внутри каждого частичные, поскольку они не могут быть унаследованы в C# (see this).

 Смежные вопросы

  • Нет связанных вопросов^_^