2012-06-28 1 views
4

У меня возникла огромная проблема с nhibernate n + 1, и ничто, что я пытаюсь, похоже, не устраняет проблему. Профилировщик Nhibernate по-прежнему показывает, что n + 1 выбирает удары по базе данных.Nhibernate n + 1 с тройными отношениями. Хотите, чтобы средняя сущность находилась в тройном

Вот моя модель:

public class CustomerGroup : CoreObjectBase 
{ 
    public virtual long GroupId { get; set; } 

    public virtual Site Site { get; set; } 

    public virtual IList<Customer> Customers { get; set; } 

    public virtual string Name { get; set; } 
    public virtual string DisplayName { get; set; } 
    public virtual CustomerGroupStatus Status { get; set; } 

    public CustomerGroup() 
    { 
     Customers = new List<Customer>(); 
    } 
} 

И мой клиент

public class Customer : CoreObjectBase 
{ 
    public virtual int CustomerId { get; set; } 
    public virtual Site Site { get; set; } 
    public virtual CustomerType CustomerType { get; set; } 
    public virtual CustomerName Name { get; set; } 
    public virtual Address Address { get; set; } 
    public virtual ContactInfo ContactInfo { get; set; } 

    public virtual IList<Invoice.Invoice> Invoices { get; set; } 

    public virtual IList<ItemBase> Payments { get; set; } 

    public virtual CustomerOptions Options { get; set; } 
} 

И варианты

public class CustomerOptions : CoreObjectBase 
{ 
    public virtual int CustomerOptionsId { get; set; } 

    private int CustomerId { get; set; } 
    private Customer Customer { get; set; } 

    public virtual bool PortalSignInDisabled { get; set; } 
    public virtual CustomerGroup Group { get; set; } 

    protected CustomerOptions() 
    { 

    } 

    public CustomerOptions(Customer customer) 
    { 
     Customer = customer; 
    } 

    public virtual Customer GetCustomer() 
    { 
     return Customer; 
    } 
} 

И, наконец, мои счета

public class Invoice : CoreObjectBase 
{ 
    public virtual long InvoiceId { get; set; } 

    private string SiteId { get; set; } 
    private string CustomerId { get; set; } 

    [Required] 
    [StringLength(50)] 
    public virtual string InvoiceNumber { get; set; } 

    public virtual decimal Amount { get; set; } 
    public virtual decimal OpenAmount { get; set; } 
    public virtual decimal ClosedAmount { get; set; } 

    public virtual InvoiceStatus Status { get; set; } 
    public virtual DateTime? DateDue { get; set; } 
    public virtual DateTime? InvoiceDate { get; set; } 

    public virtual DateTime Created { get; set; } 
    public virtual DateTime Modified { get; set; } 

    public virtual Site Site { get; set; } 
    public virtual Customer Account { get; set; } 

    public virtual IList<InvoiceLineItem> LineItems { get; set; } 
    public virtual IList<InvoicePayment> Transactions { get; set; } 


    public Invoice() 
    { 
     Created = DateTime.Now; 
     Modified = DateTime.Now; 

     Site = new Site(); 
     Account = new Customer(); 

     LineItems = new List<InvoiceLineItem>(); 
     Transactions = new List<InvoicePayment>(); 
    } 

    public override bool Equals(object obj) 
    { 
     return base.Equals(obj); 
    } 

    public override int GetHashCode() 
    { 
     return base.GetHashCode(); 
    } 
} 

А теперь мое отображение клиент

public sealed class CustomerMap : ClassMap<Customer> 
{ 
    public CustomerMap() 
    { 
     Table("Customers"); 

     Id(x => x.CustomerId).GeneratedBy.Identity(); 

     Map(x => x.CustomerType).CustomType<CustomerType>(); 
     Map(x => x.DriversLicense).CustomType<TrimmedString>(); 
     Map(x => x.LicenseState).CustomType<TrimmedString>(); 
     Map(x => x.Notes).CustomType<TrimmedString>(); 

     References<Site>(x => x.Site, "SiteId"); 

     HasOne<CustomerOptions>(x => x.Options) 
      .Cascade.All(); 

     Component(x => x.Name, y => 
     { 
      y.Map(x => x.Name1).CustomType<TrimmedString>(); 
      y.Map(x => x.Name2).CustomType<TrimmedString>(); 
     }); 

     Component(x => x.Address, y => 
     { 
      y.Map(x => x.Address1).CustomType<TrimmedString>(); 
      y.Map(x => x.Address2).CustomType<TrimmedString>(); 
      y.Map(x => x.City).CustomType<TrimmedString>(); 
      y.Map(x => x.State).CustomType<TrimmedString>(); 
      y.Map(x => x.ZipCode).CustomType<TrimmedString>(); 
      y.Map(x => x.Country).CustomType<TrimmedString>(); 
     }); 

     Component(x => x.ContactInfo, y => 
     { 
      y.Map(x => x.EMail).CustomType<TrimmedString>(); 
      y.Map(x => x.Fax).CustomType<TrimmedString>(); 
      y.Map(x => x.Phone1).CustomType<TrimmedString>(); 
      y.Map(x => x.Phone2).CustomType<TrimmedString>(); 
     }); 

     HasMany<FTNI.Core.Model.Invoice.Invoice>(x => x.Invoices) 
      .KeyColumn("CustomerId") 
      .Inverse() 
      .Cascade.All() 
      .Where("Status = 0") 
      .OrderBy("DueDate, InvoiceDate") 
      .Fetch.Join(); 
    } 
} 

и мое отображение счетов

public InvoiceMap() 
    { 
     Table("InvoiceView"); 

     Map(x => x.InvoiceId).Generated.Always(); 

     CompositeId() 
      .KeyProperty(Reveal.Member<FTNI.Core.Model.Invoice.Invoice>("SiteId")) 
      .KeyProperty(Reveal.Member<FTNI.Core.Model.Invoice.Invoice>("CustomerId")) 
      .KeyProperty(x => x.InvoiceNumber); 

     Map(x => x.Amount); 
     Map(x => x.Created).Generated.Insert(); 
     Map(x => x.ClosedAmount); 
     Map(x => x.DateDue, "DueDate"); 
     Map(x => x.InvoiceDate); 
     Map(x => x.OpenAmount); 
     Map(x => x.Status).CustomType<InvoiceStatus>(); 

     References<Site>(x => x.Site, "SiteId"); 
     References<Customer>(x => x.Account, "CustomerId"); 

     HasMany<InvoiceLineItem>(x => x.LineItems) 
      .KeyColumns.Add("SiteId", "CustomerId", "InvoiceNumber") 
      .Cascade.All(); 

     HasMany<InvoicePayment>(x => x.Transactions) 
      .Where("Status IN (0, 1)") 
      .KeyColumns.Add("SiteId", "CustomerId", "InvoiceNumber") 
      .Cascade.All(); 
    } 

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

То, что я пытаюсь сделать, это показать все счета-фактуры для всех членов группы, отделенных от клиента (заказывается по имени клиента), а затем заказывать счета-фактуры по сроку.

так, мой сайт выглядит примерно так:

Имя клиента (номер)

  • Счет 1 информация
  • Счет 2 Информация
  • Счет 3 Информация о

Следующий C ustomer (номер)

  • Счет-информационный
  • Счет Информация B
  • счет-фактура С информация

Таким образом, я сделал запрос

results = Session.CreateQuery(String.Format(@"select distinct customer from Customer customer join fetch customer.Invoices where customer.Options.Group.GroupId = {0}", 
       groupId)).List().Cast<Customer>(); 

Это все еще вызывает N +1 выпуск. Любые идеи о том, как заставить запрос работать?

В идеале, запрос будет состоять из идентификатора группы, где у клиента есть счета-фактуры (count> 0), а затем заказывается по имени клиента и дате выставления счета-фактуры. Все это кажется мне прямым - я делаю заказ и исключение, где после получения первоначального набора. Тем не менее, я все еще получаю проблему n + 1.

В профилировщике я вижу, что он делает соединение с клиентом на счета-фактуры. Тем не менее, он затем получает информацию о каждом счете-фактуре.

Я подозреваю, что это связано с тем, что в моем коде я переводил свою модель из модели данных (сопоставленной с nhibernate) в модель представления (не отображаемой в nhibernate), пытаясь отделить данные, чтобы она не перезвонила базы данных.

Мне нужно какое-то руководство по обработке данных, чтобы я мог перебирать набор данных (foreach client foreach invoice) для рендеринга моей страницы. Вот linq, который делает преобразование.

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

var customerModels = from c in customers 
    let invoices = c.Invoices 
    select new CustomerModel() 
    { 
     CustomerNumber = c.CustomerNumber, 
     CustomerId = c.CustomerId, 
     Name = c.Name.DisplayName, 
     Invoices = (from i in invoices 
        join s in selectedInvoices on i.InvoiceId equals s.Key into selected 
        from inv in selected.DefaultIfEmpty() 
        select new InvoiceModel() 
        { 
         Amount = i.Amount, 
         ClosedAmount = i.ClosedAmount, 
         DueDate = i.DateDue, 
         InvoiceDate = i.InvoiceDate, 
         InvoiceId = i.InvoiceId, 
         InvoiceNumber = i.InvoiceNumber, 
         OpenAmount = i.OpenAmount, 
         Condensed = false, 

         Selected = inv.Key > 0, 
         ReasonValue = inv.Key > 0 ? inv.Value.Item3 : String.Empty, 
         OtherReason = inv.Key > 0 ? inv.Value.Item4 : String.Empty, 
         PaymentAmount = inv.Key > 0 ? inv.Value.Item2 : i.OpenAmount 
        }).Sort(sortIndex.Value, sortOrder.Value).ToList(), 

     EnableReason = enableReasons, 
     EnableReasonSelector = enableReasonSelector, 
     Reasons = reasons, 
     Condensed = false, 

     SortIndex = sortIndex.Value, 
     SortOrder = newSortOrder 
    }; 

model.Customers = customerModels.ToList(); 

Я делаю это потому, что я предположил, что .ToList() приведет данные немедленно преобразовать и отделить от NHibernate и не должны выполнять п + 1 вызовы к базе данных. Однако по-прежнему удается выполнить эти вызовы.

ответ

2

Я вижу, что вы используете Composite ID для счета-фактуры. Возможно, вас затронул Equals() problem.

Таким образом, вы должны иметь переопределение GetHashCode() и Equals(), которое знает, как сделать сравнение со всеми свойствами составного ID.

Stuart's answer ссылки на NHibernate and Composite Keys сообщение на nhibernate.info где вы можете найти дополнительную информацию.

+1

Это была моя проблема. Я внедрил правильное переопределение «Equals» и «GetHashCode», и это сработало. Спасибо огромное! – Josh

Смежные вопросы