Свободное отображение NHibernate "многие ко многим" с помощью SQL Azure

Поскольку SQL Azure требует кластеризованных индексов для каждой таблицы для репликации (см. Здесь http://blogs.msdn.com/b/sqlazure/archive/2010/05/12/10011257.aspx) Я добавил в свой MsSqlConfiguration следующий MsSqlAzureDialect:

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

Однако это не решает проблему, с которой я сейчас сталкиваюсь с таблицей "многие ко многим". В настоящее время у меня есть ситуация, когда у меня есть пользователь с ролями и ролью с пользователями. Таким образом, существует связь «многие ко многим» между пользователями и ролями и таблица ссылок, созданная NHibernate. Итак, в моем UserAutomappingOverride у меня есть что-то вроде этого:

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 в плавную конфигурацию. Однако это не сработало из-за ошибки, в которой говорилось: «По соображениям безопасности DTD запрещен в этом XML-документе ...»

Примечание. Я использую NHibernate для создания базы данных в первую очередь кода. Итак, я фактически использую свои сущности и AutomappingOverrides для создания схемы базы данных при развертывании.

Любая помощь приветствуется.


person David Sulpy    schedule 11.06.2012    source источник


Ответы (4)


У меня есть такая структура, которая работает в 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]
person Craig    schedule 11.06.2012
comment
Я почти уверен, что он понимает конечный результат, которого он хочет, но в основном спрашивает, как сгенерировать этот скрипт, используя nhibernate и беглый nhibernate. - person nathan gonzalez; 12.06.2012
comment
Да, конечно, я могу добавить кластерные индексы в таблицы после того, как NHibernate создаст схему, но мне придется делать это вручную в SQL. Я пытаюсь понять, как использовать плавный nhibernate или nhibernate для создания кластеризованного индекса при создании таблицы. - person David Sulpy; 12.06.2012

Чтобы таблицы сопоставления были сгенерированы с использованием первичного ключа, вы можете использовать и IBag. Основываясь на моем исследовании, Fluent NHibernate еще не поддерживает это, но вы можете использовать Fluent NHibernate для сопоставления файлов hbm вместе с сопоставлениями классов и / или автоматическими сопоставлениями, чтобы получить доступ к функциям, которые напрямую не поддерживаются через Fluent NHibernate.

Вот быстрый пример, который сопоставляет пользователей с ролями:

Program.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);
    }
}

User.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>

Role.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>
person Derek Greer    schedule 12.06.2012
comment
idbag был именно тем, что мне было нужно. Очень жаль, что плавная абстракция NHibernate еще не раскрывает этого, потому что я ненавижу добавлять файлы hbm xml в мой свободный проект. Спасибо за ответ! - person David Sulpy; 13.06.2012
comment
Ах, одна вещь, которую я не заметил до сих пор ... Теперь мне нужно добавить все свои свойства для сущностей внутри узла класса файлов hbm, потому что это похоже на то, что сопоставления файлов hbm теперь имеют приоритет над моим автоматическим отображением на С # Entities. - person David Sulpy; 13.06.2012

Итак, ответ @Derek Greer был отличным. Однако я не хотел, чтобы у меня было слишком много гибридной среды nhibernate и беглой среды nhibernate.

Итак, что я в итоге сделал, поскольку все это небольшие уловки для добавления кластеризованных индексов для связывания таблиц для 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>
person David Sulpy    schedule 13.06.2012
comment
Это прекрасно работает. Важно отметить, что для XML-файла необходимо установить действие сборки на Embedded. Когда-нибудь я хотел бы иметь возможность делать это без файлов HBM ... может быть, диалект? Кто знает. - person Byron Sommardahl; 17.06.2014

Я долгое время исследовал эту тему, и, поскольку я не хотел менять беглый код nhibernate и nhibernate, я решил найти обходной путь:

У меня уже было консольное приложение, которое инициализировало среду db (локально или в лазурном), и после того, как плавное создание базы данных 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]

Надеюсь, это поможет. Амир

person Amir Yonatan    schedule 24.06.2012