Подкласс FluentNHibernate и иерархические данные - PullRequest
0 голосов
/ 11 июня 2011

У меня есть таблица учетных записей, в которой хранятся основные и дополнительные учетные записи. Основные учетные записи в основном совпадают с дополнительными учетными записями, за исключением того, что они могут иметь связанную компанию. Account является абстрактным классом, и MasterAccount и SubAccount являются его производными.

MasterAccount - это любая запись учетной записи с нулевым ParentAccountId. Если запись учетной записи имеет ParentAccountId, то это SubAccount, а ParentAccountId ссылается на поле AccountId для MasterAccount.

Я пытаюсь получить сопоставления FluentNhibernate для них.

Классы выглядят следующим образом

    public class Account : EntityBase
    {
        public Account() { }

        public virtual string AccountNumber { get; set; }

        public virtual string AccountName { get; set; }

        public virtual string ContactRole { get; set; }

        public virtual bool EmailBillDataFile { get; set; }

        public virtual bool EmailBill { get; set; }

        public virtual bool PostBill { get; set; }

        public virtual BillingMethod BillingMethod { get; set; }

        public virtual BillingAddressType BillingAddressType { get; set; }

        public virtual Contact Contact { get; set; }

        public virtual bool IsInvoiceRoot { get; set; }

        public virtual string Password { get; set; }

        public virtual bool HasRequestedInvoicing { get; set; }

        public virtual bool IsInternational { get; set; }

        public virtual decimal AmountPaid { get; set; }

        public virtual decimal PreviousBill { get; set; }

        public virtual void MakePayment(decimal amount)
        {
            MakePayment(amount, null);
        }

        public virtual void MakePayment(decimal amount, string invoiceNumber)
        {
            AmountPaid += amount;

            if (string.IsNullOrEmpty(invoiceNumber))
                LogActivity(string.Format("Made payment of {0:c}", amount));
            else {
                LogActivity(string.Format("Made payment of {0:c} on Invoice '{1}'", amount, invoiceNumber));
            }
        }

        public virtual Invoice CreateInvoice()
        {
            Invoice invoice;
            invoice = IsInternational ? new NoGstInvoice() : new Invoice();

            // Can update invoice properties that rely on account data here.


            return invoice;
        }

        #region Business Rules

        public override IEnumerable<RuleViolation> GetRuleViolations()
        {
            if (string.IsNullOrEmpty(AccountName))
                yield return new RuleViolation("Account Name required", "AccountName");

            if (string.IsNullOrEmpty(AccountNumber))
                yield return new RuleViolation("Acocunt Number required", "AccountNumber");

            if (string.IsNullOrEmpty(Password))
                yield return new RuleViolation("Password required", "Password");

            yield break;
        }

        #endregion 

    }

    public class MasterAccount : Account
    {
        private Company _company;
        private IList<SubAccount> _subAccounts;

        public MasterAccount() : this(null) { }

        public MasterAccount(Company company)
        {
            _company = company;
            _subAccounts = new List<SubAccount>();
        }

        public virtual Company Company
        {
            get { return _company;  }
        }

        public virtual IEnumerable<SubAccount> SubAccounts
        {
            get { return _subAccounts; }
        }

        public virtual SubAccount CreateSubAccount(string accountNumber, string accountName)
        {
            var subAccount = new SubAccount(this)
                                 {
                                     AccountName = accountName,
                                     AccountNumber = accountNumber,
                                     Contact = this.Contact,
                                     ContactRole = this.ContactRole,
                                     PreviousBill = 0,
                                     AmountPaid = 0,
                                     BillingAddressType = this.BillingAddressType,
                                     BillingMethod = this.BillingMethod,
                                     IsInternational = this.IsInternational,
                                     IsInvoiceRoot = false,
                                     EmailBill = this.EmailBill,
                                     EmailBillDataFile = this.EmailBillDataFile,
                                     Password = this.Password,
                                     PostBill = this.PostBill                                     
                                 };

            return subAccount;
        }     
    }

public class SubAccount : Account
    {
        private MasterAccount _masterAccount;

        public SubAccount() { }

        public SubAccount(MasterAccount master)
        {
            _masterAccount = master;
        }

        public virtual MasterAccount MasterAccount 
        { 
            get { return _masterAccount;  }
        }        
    }

Отображения, которые у меня есть:

public class AccountMap : ClassMap<Account>
{
    public AccountMap()
    {
        Table("Account");
        Id(x => x.Id).Column("AccountId").GeneratedBy.Identity();
        Map(x => x.AccountName).Length(50).Not.Nullable();
        Map(x => x.AccountNumber).Length(10).Not.Nullable();
        Map(x => x.ContactRole).Length(50);
        Map(x => x.BillingMethod).Not.Nullable();
        Map(x => x.EmailBill).Not.Nullable();
        Map(x => x.PostBill).Not.Nullable();
        Map(x => x.EmailBillDataFile).Not.Nullable();
        Map(x => x.BillingAddressType).Not.Nullable();
        Map(x => x.IsInvoiceRoot).Not.Nullable();
        Map(x => x.HasRequestedInvoicing).Not.Nullable();
        Map(x => x.IsInternational).Not.Nullable();
        Map(x => x.PreviousBill).Not.Nullable();
        Map(x => x.AmountPaid).Not.Nullable();
        Map(x => x.Password).Length(20).Not.Nullable();

        References(x => x.Contact).Column("ContactId").Not.Nullable();

        DiscriminateSubClassesOnColumn("ParentAccountId");
    }
}

public class MasterAccountMap : SubclassMap<MasterAccount>
{
    public MasterAccountMap()
    {            
        References(x => x.Company).Column("CompanyId");
        HasMany(x => x.SubAccounts).KeyColumn("ParentAccountId").Inverse().Cascade.All();
    }
}

public class SubAccountMap : SubclassMap<SubAccount>
{
    public SubAccountMap()
    {
        References(x => x.MasterAccount).Column("ParentAccountId").Not.Nullable();
    }
}

Однако, когда я выполняю следующий тест:

[Test]
public void Can_add_subAccount_to_database()
{
    var master = Session.Get<MasterAccount>(1);
    var subAccount = master.CreateSubAccount("TST123", "Test Account");

    Session.Save(subAccount);
    Session.Flush();
    Session.Clear();

    var fromDb = Session.Get<SubAccount>(subAccount.Id);
    Assert.AreNotSame(subAccount, fromDb);
}

Я получаю исключение для Session.Save (subAccount); линия.

System.ArgumentOutOfRangeException : Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index

Я не получу исключение, если закомментирую отображение ссылок в SubAccountMap.
Любая помощь по правильному отображению этих отношений приветствуется.

1 Ответ

0 голосов
/ 12 июня 2011

Кажется, мне нужно было использовать метод Formula с DiscriminateSubClassesOnColumn

DiscriminateSubClassesOnColumn("").Formula("case when parentaccountid is null then '0' else '1' end");

, а затем использовать следующее в каждом из подклассов

DiscriminatorValue("0");  // In MasterAccountMap

DiscriminatorValue("1"); // in SubAccountMap

см. http://wiki.fluentnhibernate.org/Fluent_mapping

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...