简体   繁体   English

流畅的NHibernate 3表关系,不包含主键和外键

[英]Fluent NHibernate 3 table relation without primary and foreign keys

Background Info 背景资料

I have the following class that I want to map with NHibernate: 我有以下要与NHibernate映射的类:

public class Player
{
    public virtual int Id { get; set; }
    public virtual Type Type { get; set; }
    public virtual string ScreenName { get; set; }
    public virtual bool Unsubscribed { get; set; }
}

On the database side, I have the following tables: 在数据库方面,我有以下表格:

-- New table
Player (
    int Id
    int TypeId (not null) -- foreign-key to Type table
    string ScreenName (not null) -- can be an EmailAddress, but not necessarily
)
Type (
    int Id
    string Name -- "Email", "Facebook", etc
)

The Player's ScreenName can be an email address ("foo@bar.com"), a Twitter screen-name ("@FooBar"), a Skype screenname ("foo.bar"), or anything else like that. 播放器的屏幕名称可以是电子邮件地址(“ foo@bar.com”),Twitter屏幕名称(“ @FooBar”),Skype屏幕名称(“ foo.bar”)或类似的名称。 Mapping the first three properties of Player using Fluent NHibernate is easy enough: 使用Fluent NHibernate映射Player的前三个属性非常容易:

public class PlayerMap : ClassMap<Player>
{
    public PlayerMap()
    {
        Id(x => x.Id);
        Map(x => x.ScreenName)
            .Not.Nullable();
        References(x => x.Type)
            .Column("TypeId")
    }
}

public class TypeMap : ClassMap<Type>
{
    public TypeMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);
    }
}

But the Unsubscribed property is harder, because I have to get that information from two legacy tables that I can't change and that I must access in a read-only fashion (no inserts, updates, or deletes allowed): 但是Unsubscribed属性比较难,因为我必须从两个不能更改的旧表中获取该信息,并且必须以只读方式访问(不允许插入,更新或删除):

-- Legacy tables, can't change
EmailAddress (
    int Id
    string EmailAddress (not null) -- "foo@bar.com"
)
Unsubscribed (
    int Id
    int EmailAddressId (not null) -- foreign key to EmailAddress table
)

Only email Players can be unsubscribed, so Players of other types will never have a row in either the EmailAddress nor the Unsubscribed table. 只能取消订阅电子邮件播放器,因此其他类型的播放器在EmailAddress或Unsubscribed表中都不会有一行。

These are the classes the legacy tables: 这些是旧表的类:

public class EmailAddress
{
    public virtual int Id { get; set; }
    public virtual string Value { get; set; }
    public virtual IList<Unsubscription> Unsubscriptions{ get; set; }
}

public class Unsubscription
{
    public virtual int Id { get; set; }
    public virtual EmailAddress EmailAddress { get; set; }
}

And here are their Fluent mappings: 这是他们的Fluent映射:

public class EmailAddressMap : ClassMap<EmailAddress>
{
    public EmailAddressMap()
    {
        ReadOnly();
        Id(x => x.Id);
        Map(x => x.Value)
            .Column("EmailAddress")
            .Not.Nullable();
        HasMany(x => x.Unsubscriptions)
            .KeyColumn("EmailAddressId");
    }
}

public class EmailOptOutMap : ClassMap<EmailOptOut>
{
    public EmailOptOutMap()
    {
        ReadOnly();
        Id(x => x.Id);
        References(x => x.EmailAddress)
            .Column("EmailAddressId");
    }
}

Problem 问题

The problem I'm having is trying to get the unsubscribed information for email Players. 我遇到的问题是尝试获取电子邮件播放器的未订阅信息。

The only way I can relate the Unsubscribed table to the Player table is through the intermediary EmailAddress table, matching EmailAddress.EmailAddress to Player.AddressIdentifier, but I'm having trouble trying to figure out how to do this with Fluent NHibernate. 我可以将Unsubscribed表与Player表相关联的唯一方法是通过中间的EmailAddress表,将EmailAddress.EmailAddress与Player.AddressIdentifier匹配,但是我很难弄清楚如何使用Fluent NHibernate做到这一点。

I've looked at Join for multiple tables, but all the examples I've found only deal with 2 tables, not three: 我已经查看了多个表的Join,但是我发现的所有示例仅处理2个表,而不是3个:

  1. Join on tables using Fluent NHibernate 使用Fluent NHibernate加入表格
  2. Fluent Nhibernate left join 流利的Nhibernate左加入

One possible solution would be to use a formula for the read-only Unsubscribed property to populate its value dynamically based on the data stored in the legacy tables. 一种可能的解决方案是对只读的Unsubscribed属性使用一个公式,以基于遗留表中存储的数据动态填充其值。 You could map it as follows: 您可以按以下方式映射它:

Map(x => x.Unsubscribed).Formula("(CASE WHEN EXISTS (SELECT EA.Id FROM EmailAddress EA INNER JOIN Unsubscribed ON EA.Id = Unsubscribed.EmailAddressId WHERE EA.EmailAddress = ScreenName) THEN 1 ELSE 0 END)").ReadOnly();

Of course, you can improve the select query further by adding a condition for TypeId to filter out non-email Players. 当然,您可以通过为TypeId添加一个条件以过滤掉非电子邮件播放器来进一步改善选择查询。 Plus it allows you to get rid of the legacy classes & mappings, unless it's used somewhere else in the app. 另外,除非它在应用程序中的其他地方使用,否则您可以摆脱旧有的类和映射。

To supplement Denis's answer , I just wanted to add additional documentation for how property formulas work from the Hibernate docs : 为了补充Denis的答案 ,我只想添加一些有关Hibernate文档中属性公式如何工作的文档

formula (optional): an SQL expression that defines the value for a computed property. 公式(可选):一个SQL表达式,用于定义计算属性的值。 Computed properties do not have a column mapping of their own. 计算属性没有自己的列映射。

A powerful feature is derived properties. 强大的功能是派生属性。 These properties are by definition read-only. 根据定义,这些属性是只读的。 The property value is computed at load time. 该属性值是在加载时计算的。 You declare the computation as an SQL expression. 您将计算声明为SQL表达式。 This then translates to a SELECT clause subquery in the SQL query that loads an instance: 然后,这将转换为加载实例的SQL查询中的SELECT子句子查询:

<property name="totalPrice" formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p WHERE li.productId = p.productId AND li.customerId = customerId AND li.orderNumber = orderNumber )"/>

You can reference the entity table by not declaring an alias on a particular column. 您可以通过不声明特定列的别名来引用实体表。 This would be customerId in the given example. 在给定的示例中,这将是customerId。 You can also use the nested mapping element if you do not want to use the attribute. 如果不想使用该属性,也可以使用嵌套的映射元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM