Несколько внешних ключей SQLMetal, указывающих на проблему с одной таблицей

Изменить - очистите вопрос, чтобы лучше отразить реальную проблему:

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

  • record.FK_AId
  • record.FK_BId
  • запись.FKA
  • record.FKB

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

  • record.FK_AId
  • record.FK_BId
  • запись.FKA
  • record.FKBTableNameGoesHere

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

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

Изменить 2. Итак, я подумал, что решением будет просто создать частичный класс, у которого будет свойство, названное так, как я хотел, и позволить получателю / установщику указывать на плохо названное свойство. Это сработало для выбора, но не использовало его в предложениях where и тому подобном. У ЛЮБОГО есть решение ??


person Ocelot20    schedule 17.09.2010    source источник


Ответы (4)


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

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

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

Несколько внешних ключей к одной таблице

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

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

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

person Ocelot20    schedule 20.09.2010
comment
Фактически это может быть сделано на шаг впереди, и использование _OriginalPoorlyNamedStorageVariable может быть полностью опущено. Однако я не уверен, что включение вашей собственной переменной хранилища в конструктор не вызовет каких-либо других осложнений, но я, вероятно, очень скоро узнаю ... - person jahu; 01.08.2014

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

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

Таблица: 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: Я думаю, что делаю некоторые ошибки в своем английском, мне это довольно сложно.

person Dani Rodríguez    schedule 22.05.2012

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

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

  <Association Name="State_StateAction1" Member="DestinationState" ThisKey="DestinationStateId" OtherKey="Id" Type="State" IsForeignKey="true" />
person D. Patrick    schedule 26.07.2012

Вот вариант версии 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 # (см. это) .

person jahu    schedule 13.10.2014