C# 如何在Fluent NHibernate中将一对一关系映射为复合键的一部分

C# 如何在Fluent NHibernate中将一对一关系映射为复合键的一部分,c#,nhibernate,orm,fluent-nhibernate,C#,Nhibernate,Orm,Fluent Nhibernate,我有两个表在数据库级别定义父子关系。父表有一个单列主键,子表有一个复合主键,其中一列引用父表: application table tasks table = task_id (CK) -------> = taskid (PK) = user_id (CK) = transaction_id (CK) 仅供参考,user\u id和transaction\u id列不引用其他表 我正在尝试为C#中的两个实体设置流畅的NHibernate映射: 一个应用程序有一个任务,但一

我有两个表在数据库级别定义父子关系。父表有一个单列主键,子表有一个复合主键,其中一列引用父表:

application table       tasks table
= task_id (CK) -------> = taskid (PK) 
= user_id (CK)
= transaction_id (CK)
仅供参考,
user\u id
transaction\u id
列不引用其他表

我正在尝试为C#中的两个实体设置流畅的NHibernate映射:

一个
应用程序
有一个
任务
,但一个
任务
有许多
应用程序
。正是这种关系让我心神不宁

internal class ApplicationMap : ClassMap<Application>
{
    public ApplicationMap() : base()
    {
        Schema(...);
        Table(...);

        CompositeId()
            .KeyProperty(app => app.UserId, "user_id")
            .KeyReference(app => app.Task, "task_id")
            .KeyProperty(app => app.TransactionId, "transaction_id");

        // No explicit mapping defined for "task_id"
        // Other columns mapped, but omitted for brevity
    }
}

internal class TaskMap : ClassMap<Task>
{
    public TaskMap()
    {
        Schema(DbSchema.SchemaName);
        Table(DbSchema.TableName);

        Id(task => task.Id, "taskid");

        // Other columns mapped, but omitted for brevity

        // Relations
        HasMany(task => task.Applications);
    }
}
读完后,我不知道还能尝试什么。这个问题与这个问题的区别在于子表上的外键列确实需要映射到实体中(
Application.TaskId

我搜索Fluent NHibernate文档已经有一段时间了,很难找到任何涉及复合主键的内容,尤其是涉及到与其他表的关系时

为什么同时需要
TaskId
Task
我确实偶尔需要
Application.Task
,但不是经常需要。但是,应用程序表上的复合键用作与应用程序表相关的所有其他表的复合外键引用。
TaskId
属性将被大量访问,我希望避免在应用程序和任务表上进行连接查询,而只是为了获取应用程序表中已有的值

“失败”单元测试 我在NHibernate中为此映射和存储库编写了一个单元测试,但失败了:

var app = new Application(user)
{
    TaskId = "...",
    // More properties being set...
};

db.Web.Applications.Create(app);
db.SaveChanges();

var actual = db.Web.Applications.Find(app.UserId, app.TaskId, app.TransactionId);

// Test was failing here
Assert.IsNotNull(actual.Task, "No task found");
真正的问题似乎是新插入的记录的
Task
属性为空,并且在从同一个NHibernate会话检索后没有被延迟加载(经过一些研究,这是预期的行为)


我已经经历了多次映射迭代,实际上在最初的映射中确实存在问题。我只是“一直有问题”,因为我不理解NHibernate在插入新记录时的行为。

我已经看过你的映射,当你使用复合键进行映射时,如果你使用一个键对象,它会起作用,像这样

public class ApplicationId
    {
        public virtual string UserId { get; set; }
        public virtual string TransactionId { get; set; }
        public virtual Task Task { get; set; }

        public override bool Equals(object obj)
        {
            ApplicationId recievedObject = (ApplicationId)obj;

            if ((Task.Id == recievedObject.Task.Id) &&
                (TransactionId == recievedObject.TransactionId) &&
                (UserId == recievedObject.UserId))
            {
                return true;
            }

            return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
这个映射就像

public class Application
    {
        public virtual ApplicationId Id { get; set; }
    }

    public class ApplicationClassMap : ClassMap<Application>
    {
        public ApplicationClassMap()
        {
            Table("Application");

            CompositeId<ApplicationId>(app => app.Id)
            .KeyProperty(key => key.UserId, "user_id")
            .KeyReference(key => key.Task, "task_id")
            .KeyProperty(key => key.TransactionId, "transaction_id");
        }
    }
公共类应用程序
{
公共虚拟应用程序Id{get;set;}
}

公共类应用程序ClassMap:ClassMap关于如何解决第二个问题的问题,

我认为您对
TaskClassMap
的映射需要如下:

public class TaskClassMap : ClassMap<Task>
{
    public TaskClassMap()
    {
        Table("Task");

        Id(task => task.Id, "taskid");
        HasMany(c => c.Applications)
            .KeyColumn("task_id");
    }
}
TaskId属性将被大量访问,我希望避免在application和tasks表上进行连接查询,只是为了在application表中获得一个值


此外,为了对上述声明进行评论,我要说,如果您只访问
Application.Task.Id
,nhibernate将不会查询数据库。执行延迟加载时,nhibernate会为这种类型的关系创建一个代理对象,其中存储在内存中的唯一字段是主键(
Task.Id
)。所以,如果你要访问这个字段,它实际上不会访问数据库。如果访问id之外的任何其他字段,它将触发对数据库的查询以获取剩余值。正如您在注释中所说,该值已存储在
应用程序
表中,因此,在您尝试访问仅在该表中的值之前,nhibernate不会查询
任务
表。

谢谢您的回答。我将很快尝试并让您知道。我最终选择了这个解决方案,因为当我查看代码时,
ApplicationId
类和属性感觉有点混乱,尽管这种方法也很有效。所以+1就到处都是!在玩了一些游戏之后,我删除了
映射(app=>app.TaskId,“task_id”)session.QueryOver().Where(app=>app.Task.Id==taskId)和(…)以及(…)
。现在我在执行查询时遇到了这个异常:“InvalidCastException:无法将'Task'类型的对象强制转换为'System.String'”。
public class Application
    {
        public virtual ApplicationId Id { get; set; }
    }

    public class ApplicationClassMap : ClassMap<Application>
    {
        public ApplicationClassMap()
        {
            Table("Application");

            CompositeId<ApplicationId>(app => app.Id)
            .KeyProperty(key => key.UserId, "user_id")
            .KeyReference(key => key.Task, "task_id")
            .KeyProperty(key => key.TransactionId, "transaction_id");
        }
    }
public class Task
    {
        public virtual string Id { get; set; }

        public virtual IList<Application> Applications { get; set; }
    }

    public class TaskClassMap : ClassMap<Task>
    {
        public TaskClassMap()
        {
            Table("Task");

            Id(task => task.Id, "taskid");
            HasMany<Application>(c => c.Applications);
        }
    }
public class TaskClassMap : ClassMap<Task>
{
    public TaskClassMap()
    {
        Table("Task");

        Id(task => task.Id, "taskid");
        HasMany(c => c.Applications)
            .KeyColumn("task_id");
    }
}
    CompositeId()
        .KeyProperty(app => app.UserId, "user_id")
        .KeyReference(app => app.Task, "task_id")
        .KeyProperty(app => app.TransactionId, "transaction_id");

    Map(app => app.TaskId, "task_id");