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

发布于 2024-09-19 16:21:29 字数 657 浏览 0 评论 0原文

编辑 - 清理问题以更好地反映实际问题:

我正在使用 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 子句等。有人有解决办法吗??

Edit - Cleaning up question to better reflect the actual issue:

I'm using SQLMetal to generate database classes from our SQL Server db. Recently, I needed to add a table that had multiple foreign keys pointing to the same table. Using LINQPad to play around with the new tables, I was able to access properties for both foreign keys like so:

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

...which is just how I would expect it. The problem is, the classes generated by SQLMetal produce these properties:

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

Now I could just cange the generated classes so FKBTableNameGoesHere would be FK_B, but the generated files are changed very often by different team members, so that would be a huge pain. Is there an easy fix for this?

Thanks in advance.

Edit 2
So, I thought the solution would be to just create a partial class that had a property named what I wanted it to be, and let the getter/setter point to the poorly named property. That worked for selecting, but not using it in where clauses and such. ANYONE have a solution??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蹲在坟头点根烟 2024-09-26 16:21:29

因此,我的解决方案是添加另一个分部类并添加一个属性,其中 get/set 指向奇怪命名的 FKBTableNameGoesHere 属性。这样我们就不必不断修改生成的类。不完全解决问题,但应该让开发商更清楚该房产的含义。有人看到该解决方案有任何潜在问题吗?

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

编辑 2 - 天哪,我认为这可能是一个常见问题,但我想不是。不管怎样,事实证明我的做法是正确的。我发现这篇文章:

同一个表的多个外键

这让我意识到我不能直接链接到吸气剂/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;
        }
    }
}

为任何仍能提出更清晰解决方案的人开放赏金。

So, my solution was to just add another partial class and add a property with the get/set pointed to the oddly named FKBTableNameGoesHere property. That way we don't have to keep modifying the generated classes. Not exactly solving the problem, but should make it clearer to developers what the property means. Anyone see any potential issues with that solution?

Edit - So, apparently this only works for selecting data and not filtering based on it. Not as easy of a fix as I had hoped. Anyone have any other suggestions?

Edit 2 - Jeeze, thought this would be somewhat of a common problem but I guess not. Anyway, turns out I was on the right track with this. I found this post:

Multiple foreign keys to the same table

Which gave me the idea that I couldn't just link directly to the getter/setter for another property since there's probably a lot more than that going on behind the scenes. This guys solution wasn't exactly the answer, but it sent me in the rigth direction. Adding the association attributes is what finally did it:

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

Going to leave the bounty open for anyone who can still come up with a cleaner solution.

留蓝 2024-09-26 16:21:29

好的,我将提出一个新的答案(有点晚了,抱歉),即使协会的名称发生变化,它也能起作用。

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

Orders.CustomerID 与表 Customers 引用的表:Orders 等于 Customers.Id。所以我们传递主表的Meta信息,字段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:我觉得我的英语犯了一些错误,这对我来说相当困难。

Ok, I'll propose a new answer (little late, sorry) that will work even if the name of the association changes.

This method will look for the association property of the main entity and then It will look for the value in the master table. Imagine that:

Table: Orders referenced with table Customers by Orders.CustomerID equals Customers.Id. So we pass the Meta information of the main table, the field CustomerID (which is the referenced field) and the field Name (which is the value we want).

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

And the call:

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

The problem is that only works with foreign keys with only one referenced field. But, changing to multiple keys is quite trivial.

This is a method to obtain a value of a field of the master table, you can changed to return the whole object.

PS: I think I make some mistakes about my English, it's quite difficult for me.

も让我眼熟你 2024-09-26 16:21:29

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

以下是来自 DBML 的 XML(成员属性已更改):

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

This question came in a long time ago, but I just ran into this problem. I'm using a DBML and I solved this problem by editing the relationships. If you expand the ParentProperty, you can set the property name by changing the Name property.

Here's the XML from the DBML (the Member attribute changed):

  <Association Name="State_StateAction1" Member="DestinationState" ThisKey="DestinationStateId" OtherKey="Id" Type="State" IsForeignKey="true" />
岁月静好 2024-09-26 16:21:29

这是 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,但它的工作方式就像一个魅力)。您甚至可以将此关系定义打包到基类并使部分继承它(如果您需要跨多个模型\上下文重用该关系,这应该会更容易),但问题是,您必须定义 OnCreated() 在每个部分中,因为这些不能在 c# 中继承(

Here is a variant of Ocelot20's version:

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

With this you don't even need to know the original storage name, however I'm not sure if the setter will work (I only used getter, but it worked like a charm). You can even pack this relation definition to a base class and make the partial inherit it (should you need to reuse the relation across multiple models\contexts this should make it easier), but the catch is, you will have to define OnCreated() inside each partial as those cannot be inherited in c# (see this).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文