Fluent NHibernate - Как сопоставить недействительный внешний ключ, который существует в двух соединенных таблицах

Я сопоставляю набор классов членства для своего приложения, используя Fluent NHibernate. Я сопоставляю классы с базой данных членства asp.net. Схема базы данных, относящаяся к проблеме, выглядит следующим образом:

ASPNET_USERS
UserId        PK
ApplicationId FK NOT NULL
other user columns ...

ASPNET_MEMBERSHIP
UserId        PK,FK
ApplicationID FK NOT NULL
other membership columns...

Между этими двумя таблицами существует одна-единственная связь. Я пытаюсь объединить две таблицы вместе и отобразить данные из обеих таблиц в единый объект "Пользователь", который выглядит следующим образом:

public class User
{
    public virtual Guid Id { get; set; }
    public virtual Guid ApplicationId { get; set; }

    // other properties to be mapped from aspnetuser/membership tables ...

Мой файл сопоставления выглядит следующим образом:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Table("aspnet_Users");
        Id(user => user.Id).Column("UserId").GeneratedBy.GuidComb();
        Map(user => user.ApplicationId);
        // other user mappings

        Join("aspnet_Membership", join => {
            join.KeyColumn("UserId");
            join.Map(user => user.ApplicationId);
            // Map other things from membership to 'User' class
        }
    }
}

Если я попытаюсь запустить код выше, я получаю исключение FluentConfiguration

Пытался добавить свойство "ApplicationId", когда он уже добавлен.

Если я удалю строку "Карта (user = > user.ApplicationId);" или изменить его на "Map (user = > user.ApplicationId) .Not.Update(). Not.Insert();", тогда приложение запускается, но я получаю следующее исключение при попытке вставить новый пользователь:

Невозможно вставить значение NULL в столбец "ApplicationId", таблицу "ASPNETUsers_Dev.dbo.aspnet_Users"; столбец не допускает нулей. INSERT терпит неудачу. Заявление завершено.

И если я оставил .Map(user = > user.ApplicationId), как он был первоначально, и внесет любой из этих изменений в join.Map(user = > user.ApplicationId), тогда я получить одно и то же исключение, кроме, конечно, исключение связано со вставкой в ​​таблицу aspnet_Membership

Итак... как это сделать, если я не могу изменить схему базы данных?

Ответ 1

Я думаю, что у меня есть что-то, что работает.

  public class Application
    {
        public virtual Guid ApplicationId { get; set; }


        /* Scalar Properties of an Application  */
        public virtual string ApplicationName { get; set; }
        public virtual string Description { get; set; }

        public virtual string LoweredApplicationName 
        {
            get
            {
                return this.ApplicationName.ToLower();
            }
            set
            {
                if (String.IsNullOrEmpty(this.ApplicationName))
                {
                    this.ApplicationName = value;
                }
            } 
        }

        public virtual IList<Membership> TheManyMemberships { get; protected set; }

    }


    public class User
    {
        public virtual Guid Id { get; set; }
        public virtual Application TheApplication { get; set; }

        public virtual Membership TheMembership { get; set; }

        /* Scalar Properties of a User  */
        public virtual string UserName { get; set; }
    }


    public class Membership
    {
        private Guid UserId { get; set; }
        private User _theUser { get; set; }

        protected Membership() { }

        public Membership(User theUser)
        {
            _theUser = theUser;
        }

        public virtual Application TheApplication { get; set; }

        /* Scalar Properties of a Membership  */
        public virtual string Password { get; set; }
}





    public class ApplicationMap : ClassMap<Application>
    {
        public ApplicationMap()
        {
            Table("aspnet_Applications");
            Id(app => app.ApplicationId).Column("ApplicationId").GeneratedBy.GuidComb();
            Map(x => x.ApplicationName );
            Map(x => x.LoweredApplicationName);
            Map(x => x.Description );

            HasMany<Membership>(x => x.TheManyMemberships)
                .Inverse()
                .AsBag();
        }
    }


    public class UserMap : ClassMap<User>
    {
        public UserMap()
        {
            Table("aspnet_Users");
            Id(user => user.Id).Column("UserId").GeneratedBy.GuidComb();
            References(x => x.TheApplication, "ApplicationId")
                  .Not.Nullable();

            HasOne(x => x.TheMembership)
            .Cascade.All();//
            //.Constrained();

            Map(x => x.UserName).Not.Nullable();

        }
    }


   public class MembershipMap : ClassMap<Membership>
    {
        public MembershipMap()
        {
            Table("aspnet_Membership");

            Id(Reveal.Member<Membership>("UserId"))
                .GeneratedBy.Foreign("_theUser");
            HasOne(
              Reveal.Member<Membership, User>("_theUser"))
                    .Constrained()
                    .ForeignKey();

            References<Application>(x => x.TheApplication, "ApplicationId")
            .Not.Nullable();

            Map(x => x.Password);

        }
    }

Простите некоторые из соглашений об именах, при прототипировании я использую не-неоднозначные имена по правильному соглашению, чтобы избежать путаницы.

DDL, который у меня есть (из приведенного выше кода) и DDL из вывода asp.net(4.0) (с использованием aspnet_regsql.exe для создания DDL), кажутся последовательными (между двумя версиями).

Мне нужно поблагодарить этот пост: http://brunoreis.com/tech/fluent-nhibernate-hasone-how-implement-one-to-one-relationship/

Если вы делаете какие-либо изменения, пожалуйста, напишите их.

Но я смог сохранить приложение, пользователя и членство.

Тем не менее, я думаю, что я могу немного отойти от отношения User: Membership. Сценарий microsoft выглядит как "Имейте пользователя, но позволяйте этому пользователю иметь другой пароль для каждого приложения", что имеет смысл. Но иногда при использовании кода MembershipProvider (код MS, ничего общего с NHibernate, я "чувствую", как будто иногда он принимает одно приложение.

Я чувствую, что MS DDL должен иметь уникальное ограничение на dbo.Membership(UserId, ApplicationId), но я не вижу его в своем DDL.

Несмотря на это, это должно обеспечить некоторую пищу для размышлений.

Ответ 2

Вы попробовали наследование:

public class User
....

public class Member : User
....

?

Любая причина, по которой вы вообще присоединяетесь к ApplicationId? Я считаю, что это в обеих таблицах для справки. Поскольку UserId является Guid, он уникален. Если у вас есть ситуация, когда вам нужно хранить одного и того же пользователя для двух разных приложений, хорошо, что членство asp.net не может работать так, оно создало бы две записи разных пользователей. Поиск по имени пользователя/паролю проверяет идентификатор приложения на основе настройки веб-приложений (машинный ключ), чтобы обеспечить его уникальность. Таблица членства - красная селедка.