У меня возникла огромная проблема с 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 вызовы к базе данных. Однако по-прежнему удается выполнить эти вызовы.
Это была моя проблема. Я внедрил правильное переопределение «Equals» и «GetHashCode», и это сработало. Спасибо огромное! – Josh