SQLMetal指向一个表问题的多个外键

时间:2010-09-17 14:04:37

标签: linq-to-sql .net-4.0 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,但生成的文件经常被不同的团队成员更改,因此这将是一个巨大的痛苦。对此有一个简单的解决方法吗?

提前致谢。

编辑2 所以,我认为解决方案是创建一个具有名为我想要的属性的部分类,并让getter / setter指向命名不佳的属性。这有助于选择,但不能在where子句中使用它。任何人都有解决方案??

4 个答案:

答案 0 :(得分:4)

所以,我的解决方案是添加另一个部分类并添加一个属性,其中get / set指向奇怪命名的FKBTableNameGoesHere属性。这样我们就不必继续修改生成的类了。不完全解决问题,但应该让开发人员更清楚属性意味着什么。有人发现该解决方案存在任何潜在问题吗?

编辑 - 显然,这仅适用于选择数据而不是基于它的过滤。不像我希望的那样容易修复。有人有任何其他建议吗?

编辑2 - Jeeze,认为这会是一个常见的问题,但我想不是。无论如何,事实证明我是在正确的轨道上。我找到了这篇文章:

Multiple foreign keys to the same table

这给了我一个想法,我不能直接链接到另一个属性的getter / setter,因为它可能比幕后更多。这个家伙解决方案并不完全是答案,但它让我处于严谨的方向。添加关联属性是最终做到的:

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

为那些仍能提出更清洁解决方案的人打开奖金。

答案 1 :(得分:0)

好的,即使关联名称发生变化,我也会提出一个新的答案(很晚,很抱歉)。

此方法将查找主实体的关联属性,然后它将查找主表中的值。想象一下:

OrdersCustomers引用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:我觉得我的英语犯了一些错误,这对我来说很难。

答案 2 :(得分:0)

很久以前出现了这个问题,但我刚遇到这个问题。我正在使用DBML,我通过编辑关系解决了这个问题。如果展开ParentProperty,可以通过更改Name属性来设置属性名称。

这是来自DBML的XML(会员属性已更改):

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

答案 3 :(得分: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;
        }
    }
}

有了这个,你甚至不需要知道原始的存储名称,但是我不确定setter是否可以工作(我只使用了getter,但它就像魅力一样)。您甚至可以将此关系定义打包到基类并使其部分继承它(如果您需要重用多个模型\上下文中的关系,这应该会更容易),但是问题是,您必须定义{{1在每个部分内部,因为那些不能在c#(see this)中继承。