Many-to-Many fluent NHibernate mapping with SQL Azure

本文关键字:SQL Azure with mapping fluent NHibernate Many-to-Many | 更新日期: 2023-09-27 18:36:55

由于SQL Azure要求每个表都有聚集索引以进行复制(请参阅此处 http://blogs.msdn.com/b/sqlazure/archive/2010/05/12/10011257.aspx),因此我将以下MsSqlAzureDialect添加到我的MsSqlConfiguration中:

public class MsSqlAzureDialect : MsSql2008Dialect
{
    public override string PrimaryKeyString
    {
        get { return "primary key CLUSTERED"; }
    }
}

但是,这并不能解决我目前遇到的多对多表问题。我目前遇到一种情况,即我有一个具有角色的用户,一个具有用户的角色。因此,用户和角色之间存在多对多关系,以及由 NHibernate 生成的链接表。所以在我的用户自动映射覆盖中,我有这样的东西:

public class UserAutomappingOverride : IAutoMappingOverride<User>
{
    public void Override(AutoMapping<User> mapping)
    {
        mapping.HasManyToMany(x => x.Roles).Cascade.SaveUpdate();
    }
}

这会导致 NHibernate 创建一个名为 RoleToUser 的链接表。但是,此表没有 PK,因此没有聚集索引。这是在 SQL Azure 中使用的无效表配置。

我尝试使用来自非流畅 NHibernate 的数据库对象,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<nhibernate-mapping>
    <database-object>
        <create>create clustered index ix on RoleToUser(User_id, Role_id)</create>
        <drop>drop index RoleToUser.ix</drop>
    </database-object>
</nhibernate-mapping>

但这是在黑暗中尝试将不流畅的NHibernate代码组合在一个流畅的配置中的一枪。但是,它不起作用,因为出现一个错误,指出"出于安全原因,此 XML 文档中禁止使用 DTD..."

注意:我正在使用流畅的NHibernate进行代码优先数据库创建。所以我实际上正在使用我的实体和自动映射替代来生成部署时数据库的架构。

任何帮助将不胜感激。

Many-to-Many fluent NHibernate mapping with SQL Azure

我有一个这样的结构,可以在 Azure 中工作

CREATE TABLE [dbo].[ClientLabel](
    [ClientId] [bigint] NOT NULL,
    [LabelId] [bigint] NOT NULL,
 CONSTRAINT [PK_ClientLabel] PRIMARY KEY CLUSTERED 
(
    [ClientId] ASC,
    [LabelId] ASC
) WITH (..) ON [PRIMARY]
) ON [PRIMARY]

要使用主键生成映射表,您可以使用 和 IBag。 根据我的研究,Fluent NHibernate还不支持此功能,但是您可以使用Fluent NHibernate将hbm文件与类映射和/或自动映射一起映射,以便获得Fluent NHibernate不直接支持的功能。

下面是将用户映射到角色的快速示例:

程序.cs

class Program
{
    static void Main(string[] args)
    {
        DatabaseContextFactory.CreateSchema();
        var user = new User();
        user.Roles = new List<Role>();
        var role = new Role();
        user.Roles.Add(role);
        ISessionFactory sessionFactory = DatabaseContextFactory.CreateSessionFactory();
        using (ISession session = sessionFactory.OpenSession())
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                session.SaveOrUpdate(role);
                session.SaveOrUpdate(user);
                transaction.Commit();
            }
        }
    }
}

DatabaseContextFactory.cs

public static class DatabaseContextFactory
{
    static ISessionFactory _sessionFactory;
    static Configuration _configuration;
    public static ISessionFactory CreateSessionFactory()
    {
        if (_sessionFactory == null)
        {
            _sessionFactory = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008.ConnectionString(cs => cs.FromConnectionStringWithKey("SurrogateExample")))
                .ExposeConfiguration(c => _configuration = c)
                .Mappings(cfg => cfg.HbmMappings.AddFromAssemblyOf<Program>())
                .BuildSessionFactory();
        }
        return _sessionFactory;
    }
    public static void CreateSchema()
    {
        CreateSessionFactory();
        new SchemaExport(_configuration).Execute(false, true, false);
    }
}

用户.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                                     assembly="SurrogateManyToManyExample"
                   namespace="SurrogateManyToManyExample.Entities">
    <class name="User" table="[User]">
        <id name="Id">
            <generator class="guid.comb" />
        </id>
        <idbag name="Roles" table="UserInRole" lazy="true">
            <collection-id column="Id" type="Guid">
                <generator class="guid.comb" />
            </collection-id>
            <key column="UserId" />
            <many-to-many column="RoleId" class="Role" fetch="join" />
        </idbag>
    </class>
</hibernate-mapping>

角色.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                                     assembly="SurrogateManyToManyExample"
                   namespace="SurrogateManyToManyExample.Entities">
    <class name="Role" table="[Role]">
        <id name="Id">
            <generator class="guid.comb" />
        </id>       
    </class>
</hibernate-mapping>

所以@Derek格里尔的回答很棒。但是,我不希望拥有太多混合的冬眠和流畅的冬眠环境的混乱。

因此,由于这些都是添加聚集索引以链接SQL Azure表的小技巧,所以我最终所做的是添加一个专用于添加聚集索引的hbm文件。这样,每当将多对多关系流畅地添加到自动映射中的对象时,开发人员都可以手动向文件添加一行,如下所示。这实际上非常接近我之前找到的答案,但我只是对非流畅的 nhibernate 不够了解,无法知道我在加载 hbm 文件时做错了什么。所以,这里是:

ManyToManyClusteredIndexes.hbm.xml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Bookstore.Data.HbmMappings"
assembly="Bookstore.Data.HbmMappings">
    <database-object>
        <create>create clustered index ix on RoleToUser(User_id, Role_id)</create>
        <drop>drop index RoleToUser.ix</drop>
    </database-object>
    <database-object>
        <create>create clustered index ix on RoleToRoleGroup(RoleGroup_id, Role_id)</create>
        <drop>drop index RoleToRoleGroup.ix</drop>
    </database-object>
</hibernate-mapping>

我已经研究了很长时间关于这个主题,由于我不想更改流利的 nhibernate 和 nhibernate 代码,我决定进行以下解决方法:

我已经有一个控制台应用程序正在初始化数据库环境(本地或 azure),在流畅的 nhibernate 数据库创建结束后,我只是删除了 ManyToMany 表,并按照 Azure 的期望重新创建它们:

drop table ProfileLikes
CREATE TABLE ProfileLikes(
    [ProfileID] [bigint] NOT NULL,
    [LikeID] [bigint] NOT NULL,
    PRIMARY KEY CLUSTERED 
(
    [ProfileID],[LikeID]  ASC
)
) 
ALTER TABLE ProfileLikes  WITH CHECK ADD  CONSTRAINT [FK6FB4BC1C85106EB3] FOREIGN KEY([LikeID])
REFERENCES [dbo].[Likes] ([Id])
ALTER TABLE ProfileLikes CHECK CONSTRAINT [FK6FB4BC1C85106EB3]
ALTER TABLE ProfileLikes  WITH CHECK ADD  CONSTRAINT [FK6FB4BC1CA12A6EC5] FOREIGN KEY([ProfileID])
REFERENCES Profiles ([Id])
ALTER TABLE ProfileLikes CHECK CONSTRAINT [FK6FB4BC1CA12A6EC5]

希望对您有所帮助。阿米尔