間違いなく発見したように、継承を次のようにマップするのは簡単です:Person-> User、またはPerson-> Manager、またはPerson-> Manager-> User(または、Person-> Manager-> User)。
NHibernateでは、サブクラスへの昇格/サブクラスからの降格は許可されていません。昇格または降格するには、ネイティブSQLを実行する必要があります。
ただし、継承の最初の「マップ」に従った場合は、実行しようとしていることに対してサブクラスを使用することは、モデル化しようとしていることに対して不適切な解決策であるというエピファニーがあったはずです。そして、それは2つのサブクラスだけです!役割を追加するとどうなりますか?
あなたが持っているのは、役割が拡張可能な、任意の数の役割のメンバーになることができる人です。このソリューションを検討してください(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
時間を節約するために、両方のプロジェクトの流暢なマッピングを1つにまとめました。モデルと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を特定のロールの1つのインスタンスに制限する場合は、一意のインデックスを設定し、EqualsメソッドをいじってId
、同じか同じかを確認し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);
また、他のアセンブリが追加の役割を定義できることもわかります。Managerは、モデルとは異なるアセンブリにあります。
したがって、これは別のアプローチを使用しているという事実にもかかわらず、これがあなたが望むすべてに加えてさらに多くのことを行うことがわかります。