正如您无疑发现的那样,像这样映射继承很容易:Person -> User,或 Person -> Manager,或 Person -> Manager -> User(或者,Person -> Manager -> User)。
NHibernate 不允许您从子类升级/降级。您必须运行本机 SQL 才能升级或降级。
但是,如果您遵循我最初的继承“地图”,您应该有一个顿悟,即使用子类来做您正在尝试做的事情对于您要建模的事情是不合适的解决方案。这只是两个子类!当您添加更多角色时会发生什么?
你所拥有的是一个 Person,他可以是任意数量的角色的成员,其中角色是可扩展的。考虑这个解决方案(github上的来源:https ://github.com/HackedByChinese/NHibernateComposition ):
(假设我们有一个处理相等性的实体抽象类,其中具有相同 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
我将两个项目的流利映射合二为一以节省时间。可以很容易地为模型和模型建立单独的映射程序集。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
相同 ORRoleName
是否相同。
您可以轻松获取或检查任何类型的用户角色:
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);
您还可以看到其他程序集可以定义其他角色。经理与模型在不同的装配中。
所以,你可以看到它会做你想做的一切以及更多,尽管它使用了不同的方法。