[英]Nhibernate n+1 with ternary relationship. Want the middle entity in the ternary
I'm having a huge issue with nhibernate n+1 and nothing I try seems to fix the problem. 我在使用nhibernate n + 1遇到了一个大问题,但我尝试解决的所有问题似乎都没有。 Nhibernate profiler still shows n+1 selects hitting the database.
Nhibernate Profiler仍然显示n + 1个选择命中了数据库。
Here's my model: 这是我的模型:
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>();
}
}
And my 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; }
}
And the options 和选项
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;
}
}
And finally, my invoices 最后,我的发票
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();
}
}
And now my customer mapping 现在我的客户映射
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();
}
}
and my invoices mapping 和我的发票映射
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();
}
I have to join to the other tables on those three fields because one of our clients completely dumps their data and reloads all invoices from scratch (don't ask why). 我必须加入这三个字段上的其他表,因为我们的一位客户完全转储了他们的数据并从头开始重新加载了所有发票(不要问为什么)。 So, in order to maintain connectivity, I join to those tables on fields that allow the new invoice to hook back up with the refreshed data.
因此,为了保持连接性,我加入了字段中的那些表,这些表允许新发票与刷新的数据挂钩。
What I am trying to do is show all invoices for all members in a group, separated by customer (ordered on customer name) and then have the invoices ordered by due date. 我想做的是显示组中所有成员的所有发票,并按客户分开(按客户名称订购),然后按到期日期订购发票。
so, my site looks something like this: 因此,我的网站看起来像这样:
Customer Name (number) 客户名称(编号)
Next Customer (number) 下一位顾客(人数)
So, I made a query 所以,我做了一个查询
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>();
This still causes an N+1 issue. 这仍然会导致N + 1问题。 Any ideas on how to make the query work?
关于如何使查询工作的任何想法?
Ideally, The query would be by group Id where the customer has invoices (count > 0), then ordered by customer name and invoice due date. 理想情况下,查询将按组ID进行,其中客户有发票(计数> 0),然后按客户名称和发票到期日期排序。 That all seems straight forward to me -- I am doing the ordering and exclusionary where after I get the initial set.
这一切对我来说似乎都是直截了当的-我正在进行排序和排除,在获得初始设置之后。 However, I still get the n+1 problem.
但是,我仍然遇到n + 1问题。
In the profiler, I see it doing the join from the customer to the invoices. 在分析器中,我看到它正在执行从客户到发票的联接。 However, it then proceeds to get the details of each invoice.
但是,然后继续获取每个发票的详细信息。
I suspect this is because in my code I translate my model from the Data Model (mapped to nhibernate) to a view model (not mapped to nhibernate) in an attempt to separate the data so that it will not call back to the database. 我怀疑这是因为在我的代码中我试图将我的模型从数据模型(映射到nhibernate)转换为视图模型(未映射到nhibernate),以试图分离数据,以便它不会回调到数据库。
I need some guidance on how to handle the data so I can loop through the data set (foreach customer foreach invoice) to render my page. 我需要一些有关如何处理数据的指导,以便可以遍历数据集(foreach客户foreach发票)以呈现我的页面。 Here is the linq that does the transformation.
这是执行转换的linq。
selected invoices is a dictionary storing invoices that are selected on the front end to be paid. 选定发票是一个词典,其中存储在前端选择要支付的发票。 When I load the page, I want to grab the selected invoices to not if they are being paid, how much is being applied, and some other information.
当我加载页面时,我想不获取所选发票,是否已支付,应用了多少以及其他一些信息。
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();
I do this because I assumed that the .ToList()
would cause the data to transform immediately and separate from nhibernate and not have to perform the n+1 calls to the database. 我这样做是因为我假设
.ToList()
会导致数据立即进行转换并与nhibernate分离,而不必执行对数据库的n + 1调用。 However it still manages to make those calls. 但是,它仍然设法发出这些呼叫。
I see that you are using Composite ID for Invoice. 我看到您正在使用复合ID发票。 Maybe you are affected with the
Equals()
problem . 也许您受到
Equals()
问题的影响 。
In summary, you must have an override of GetHashCode()
and Equals()
that knows how to do comparison on all properties of composite ID. 总而言之,您必须具有
GetHashCode()
和Equals()
的重写,该重写知道如何对组合ID的所有属性进行比较。
Stuart's answer links to the NHibernate and Composite Keys post on nhibernate.info where you can find additional information. Stuart的答案链接到nhibernate.info上的NHibernate和Composite Keys帖子,您可以在其中找到更多信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.