Fluent NHibernate:来自单个查询的 IEnumerable 组件映射?

发布于 2024-09-07 21:01:38 字数 548 浏览 1 评论 0原文

希望您能帮助我解决我遇到的映射问题。目前我有一个视图返回诸如以下内容:

名称|标题 |价格|日期 |格式|格式优先级 |

示例数据可能是:

Bob |学分| 340 | 340 2010 年 1 月 1 日 |酒吧| 10 |
鲍勃 |学分| 340 | 340 2010 年 1 月 1 日 | FOO | 20 |
鲍勃 |学分| 340 | 340 2010 年 1 月 1 日 |呜| 40 |

我想要的是一个如下所示的域模型:

string Name;
字符串标题;
价格;
日期时间日期;
IEnumerable 格式;

格式类将具有:

字符串类型
int Priority

目前我们在 Fluent NHibernate 中使用 ClassMap 方法(不是自动配置)。我们如何绘制这个图?该组件似乎不支持集合,并且这不是 HasMany 关系,因为它作为同一查询的一部分返回。

有什么想法吗?

谢谢

Hoping you can help me with a mapping problem I have. At the moment I have a view which returns something such as:

Name | Title | Price | Date | Format | FormatPriority |

Example data may be:

Bob | Credits | 340 | 01/01/2010 | BAR | 10 |
Bob | Credits | 340 | 01/01/2010 | FOO | 20 |
Bob | Credits | 340 | 01/01/2010 | WOO | 40 |

What I want is a domain model which looks like this:

string Name;
string Title;
int Price;
DateTime Date;
IEnumerable Formats;

Format class would then have:

string Type
int Priority

At the moment we are using the ClassMap approach within Fluent NHibernate (not auto config). How would we map this? The Component doesn't seem to support a collection and this isn't a HasMany relationship as it's coming back as part of the same query.

Any ideas??

Thanks

Ben

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

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

发布评论

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

评论(1

肥爪爪 2024-09-14 21:01:38

免责声明:这是一个巨大的黑客攻击,我很难发布它。

这是基于您提供的架构,因此可能需要对其进行修改以适应不同的设计。可能有更好的方法来做到这一点,但希望这至少能让你再次前进。

问题是您的模型和查询有点不匹配。您的查询返回多行,您打算将其用于具有多个组件的单个实体,但 NHibernate 适合将其解释为多个实体,每个实体具有单个组件。

NHibernate 支持组件集合,但仅当它们存储在单独的表/视图中时才支持。这些组件通过外键连接回实体表。 如果您可以更改您的设计来支持这一点,请这样做!

如果不能,我能想到的唯一选择就是在您的视图上进行自连接。它不会产生最优化的查询,但它应该可以解决问题。

您没有提及您的实体的名称,因此我选择了 Transaction

public class Transaction
{
    public virtual string Name { get; set; }
    public virtual string Title { get; set; }
    public virtual decimal Price { get; set; }
    public virtual DateTime Date { get; set; }
    public virtual ISet<Format> Formats { get; set; }
}

public class Format
{
    public virtual string Type { get; set; }
    public virtual int Priority { get; set; }

    // OVERRIDE EQUALITY MEMBERS!
}

我使用的映射是:

public class TransactionMap : ClassMap<Transaction>
{
    public TransactionMap()
    {
        Table("vwTransactions");
        Id(x => x.Name);
        Map(x => x.Title);
        Map(x => x.Price);
        Map(x => x.Date);
        HasMany(x => x.Formats)
            .Table("vwTransactions")
            .KeyColumn("Name")
            .Component(c =>
            {
                c.Map(x => x.Type, "Format");
                c.Map(x => x.Priority, "FormatPriority");
            })
            .Fetch.Join();
    }
}

因此您可以看到映射指向 vwTransactions 视图。您没有在架构中指定 id,因此我使用 Name 作为身份(这很重要)。现在跳到HasMany,你可以看到它也指向vwTransactions; NHibernate 将看到这一点并在视图上进行自连接。然后将键列设置为Name,与实体Id相同;这样 NHibernate 将使用它来解析组件和实体之间的引用,而不是尝试使用整数外键。 Fetch.Join 将强制 NH 急切地获取此关系,因此至少我们可以节省一些时间。最后需要注意的是,Formats 属性是一个 ISet,如果您不这样做,您最终会得到重复的组件。

如果您现在为 Transaction 创建条件(或 hql)查询,您将返回实体及其组件;但是,由于每个实体返回多行,您会得到重复项。这是相当常见的,并且可以使用 DistinctRootEntity 转换器轻松解决。

var transactions = session.CreateCriteria(typeof(Transaction))
  .SetResultTransformer(Transformers.DistinctRootEntity)
  .List<Transaction>();

应该就是这样,您现在最终将得到一个包含 3 个组件的实体(基于您的数据集)。

令人讨厌,我知道。

Disclaimer: This is such a huge hack, it pains me to post it.

This is based on the schema you've provided, so it might need to be modified to accomodate a different design. There probably is a much better way to do this, but hopefully this should get you going again at least.

The issue is you have a bit of a mismatch in your model and query. Your query returns multiple rows which you intend to be for a single entity with multiple components, but NHibernate is geared to interpret that as multiple entities each with a single component.

NHibernate supports collections of components, but only when they're stored in a separate table/view. These components are joined via a foreign key back to the entity table. If you can change your design to support this, please do so!

If not, the only option I could think of is a self-join on your view. It won't produce the most optimised query, but it should do the trick.

You didn't mention what your entity was called, so I've gone with Transaction.

public class Transaction
{
    public virtual string Name { get; set; }
    public virtual string Title { get; set; }
    public virtual decimal Price { get; set; }
    public virtual DateTime Date { get; set; }
    public virtual ISet<Format> Formats { get; set; }
}

public class Format
{
    public virtual string Type { get; set; }
    public virtual int Priority { get; set; }

    // OVERRIDE EQUALITY MEMBERS!
}

The mapping I've used is:

public class TransactionMap : ClassMap<Transaction>
{
    public TransactionMap()
    {
        Table("vwTransactions");
        Id(x => x.Name);
        Map(x => x.Title);
        Map(x => x.Price);
        Map(x => x.Date);
        HasMany(x => x.Formats)
            .Table("vwTransactions")
            .KeyColumn("Name")
            .Component(c =>
            {
                c.Map(x => x.Type, "Format");
                c.Map(x => x.Priority, "FormatPriority");
            })
            .Fetch.Join();
    }
}

So you can see the mapping is pointed at the vwTransactions view. You didn't specify an id in your schema, so I've used Name as a identity (this is important). Skip down to the HasMany now, you can see that also points at vwTransactions; NHibernate will see this and do a self-join on the view. Then the key column is set to Name, the same as the entity Id; this way NHibernate will use that to resolve the references between the component and the entity, rather than trying to use an integer foreign key. The Fetch.Join will force NH to eagerly fetch this relationship, so at least we save a bit there. Last thing of note, the Formats property is an ISet, if you don't do this you'll end up with duplicate components.

If you now create a criteria (or hql) query for Transaction, you'll get back your entities with their components; however, you'll get duplicates due to the multiple rows being brought back per entity. This is fairly common, and easily resolved using the DistinctRootEntity transformer.

var transactions = session.CreateCriteria(typeof(Transaction))
  .SetResultTransformer(Transformers.DistinctRootEntity)
  .List<Transaction>();

That should be it, you'll now end up with just one entity (based on your dataset) with 3 components.

Nasty, I know.

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