如何为共享存储在另一个表中的数据的2个实体设置NHibernate映射?

时间:2012-05-14 12:27:19

标签: c# nhibernate fluent-nhibernate nhibernate-mapping

给出以下类层次结构:

Class Diagram http://img535.imageshack.us/img535/4802/personusermanager.jpg

其他信息:

  • Person类不是抽象的。
  • 一个人可以是用户,经理或其他实现IPerson界面的人。
  • Person类不得对其子类有任何了解。
  • 子类可以驻留在另一个程序集中。
  • 个人也可能是用户和经理。在这种情况下,UserRepository必须返回给定PersonId的User对象,ManagerRepository必须返回相同PersonId的Manager。
  • 还必须能够通过PersonRepository获取实现IPerson接口的所有对象的Person(基础)部分。

如何在NHibernate中映射?

我们正在使用FluentNHibernate 1.2和NHibernate 3.1。

在我们目前的情况下,每个班级都有自己的表格。所以我们有一个Person表,一个User表和一个Manager表。

我已经尝试了以下选项但没有成功:

  • 使用继承映射(每个子类一个表)映射;
  • 在User和Manager的映射中与Person的连接(没有继承映射);
  • 在User和Manager映射中使用Person的HasOne映射(没有连接和继承映射);

1 个答案:

答案 0 :(得分:2)

正如您无疑发现的那样,很容易映射这样的继承:Person - >用户或个人 - >经理或人 - >经理 - >用户(或者,人 - >经理 - >用户)。

NHibernate不允许您升级到子类或从子类升级。您必须运行本机SQL才能升级或降级。

但是,如果你按照我的继承的初始“地图”,你应该有一个顿悟,即你正在尝试做的事情使用子类是一个不合适的解决方案,你正在尝试建模。这只有两个子类!添加更多角色会发生什么?

你拥有的是一个人,他可以是任意角色的成员,角色是可扩展的。考虑这个解决方案(源于github:https://github.com/HackedByChinese/NHibernateComposition):

(假设我们有一个处理相等性的Entity抽象类,相同ID的相同类型的对象在哪里被视为相等)

项目:模型

public class Person : Entity, IPerson
{
    public virtual string FirstName { get; set; }

    public virtual string LastName { get; set; }

    public virtual IList<Role> Roles { get; protected set; }

    public Person()
    {
        Roles = new List<Role>();
    }

    public virtual void AddRole(Role role)
    {
        if (Roles.Contains(role)) return;

        role.Person = this;

        Roles.Add(role);
    }

    public virtual void RemoveRole(Role role)
    {
        if (!Roles.Contains(role)) return;

        role.Person = null;

        Roles.Remove(role);
    }
}

public interface IPerson
{
    string FirstName { get; set; }

    string LastName { get; set; }

    Int32 Id { get; }
}

public abstract class Role : Entity
{
    public virtual Person Person { get; set; }

    public virtual string RoleName { get; protected set; }
}

public class User : Role
{
    public virtual string LoginName { get; set; }

    public virtual string Password { get; set; }
}

项目:Models.B

public class Manager : Role
{
    public virtual string Division { get; set; }

    public virtual string Status { get; set; }
}

项目:Models.Impl

我将两个项目的流畅映射合二为一,以节省时间。对于Models和Models.B

,很容易有单独的映射程序集
public class PersonMap : ClassMap<Person>
{
    public PersonMap()
    {
        Id(c => c.Id)
            .GeneratedBy.HiLo("100");

        Map(c => c.FirstName);
        Map(c => c.LastName);

        HasMany(c => c.Roles)
            .Inverse()
            .Cascade.AllDeleteOrphan();
    }
}

public class RoleMap : ClassMap<Role>
{
    public RoleMap()
    {
        Id(c => c.Id)
            .GeneratedBy.HiLo("100");

        DiscriminateSubClassesOnColumn<string>("RoleName");

        References(c => c.Person);
    }
}

public class UserMap : SubclassMap<User>
{
    public UserMap()
    {
        DiscriminatorValue("User");

        Join("User", joined =>
                         {
                             joined.Map(c => c.LoginName);
                             joined.Map(c => c.Password);
                         });
    }
}

项目:Models.Impl.Tests

[TestFixture]
public class MappingTests
{
    private ISessionFactory _factory;

    #region Setup/Teardown for fixture

    [TestFixtureSetUp]
    public void SetUpFixture()
    {
        if (File.Exists("test.db")) File.Delete("test.db");

        _factory = Fluently.Configure()
            .Database(() => SQLiteConfiguration.Standard
                                .UsingFile("test.db")
                                .ShowSql()
                                .FormatSql())
            .Mappings(mappings => mappings.FluentMappings
                                      .AddFromAssemblyOf<PersonMap>())
            .ExposeConfiguration(config =>
                                     {
                                         var exporter = new SchemaExport(config);
                                         exporter.Execute(true, true, false);
                                     })
            .BuildSessionFactory();
    }

    [TestFixtureTearDown]
    public void TearDownFixture()
    {
        _factory.Close();
    }

    #endregion

    #region Setup/Teardown for each test

    [SetUp]
    public void SetUpTest()
    {
    }

    [TearDown]
    public void TearDownTest()
    {
    }

    #endregion

    [Test]
    public void Should_create_and_retrieve_Person()
    {
        var expected = new Person
        {
            FirstName = "Mike",
            LastName = "G"
        };

        using (var session = _factory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            session.SaveOrUpdate(expected);

            tx.Commit();
        }

        expected.Id.Should().BeGreaterThan(0);

        using (var session = _factory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            var actual = session.Get<Person>(expected.Id);

            actual.Should().NotBeNull();
            actual.ShouldHave().AllProperties().EqualTo(expected);
        }

    }

    [Test]
    public void Should_create_and_retrieve_Roles()
    {
        // Arrange
        var expected = new Person
                         {
                             FirstName = "Mike",
                             LastName = "G"
                         };

        var expectedManager = new Manager
                           {
                               Division = "One",
                               Status = "Active"
                           };
        var expectedUser = new User
                               {
                                   LoginName = "mikeg",
                                   Password = "test123"
                               };

        Person actual;

        // Act
        expected.AddRole(expectedManager);
        expected.AddRole(expectedUser);

        using (var session = _factory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            session.SaveOrUpdate(expected);

            tx.Commit();
        }

        using (var session = _factory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            actual = session.Get<Person>(expected.Id);

            // ignore this; just forcing the Roles collection to be lazy loaded before I kill the session.
            actual.Roles.Count();
        }

        // Assert
        actual.Roles.OfType<Manager>().First().Should().Be(expectedManager);
        actual.Roles.OfType<Manager>().First().ShouldHave().AllProperties().But(c => c.Person).EqualTo(expectedManager);

        actual.Roles.OfType<User>().First().Should().Be(expectedUser);
        actual.Roles.OfType<User>().First().ShouldHave().AllProperties().But(c => c.Person).EqualTo(expectedUser);
    }
}

如果要将Person约束为特定角色的一个实例,只需放置一个唯一索引并使用Equals方法混乱来检查Id是否相同OR RoleName是否相同。

您可以轻松获取或检查用户的任何类型的角色:

if (person.Roles.OfType<User>().Any())
{
   var user = person.Roles.OfType<User>().FirstOrDefault();
} 

您还可以直接查询角色以查找其人员:

var peopleWhoAreManagersInDistrictOne = (from role in session.Query<Manager>()
                                         where role.District == "One"
                                         select role.Person);

您还可以看到其他程序集可以定义其他角色。管理器与模型不同。

所以,你可以看到这会做你想要的一切以及更多,尽管它使用了不同的方法。

相关问题