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つがデータを完全にダンプし、すべての請求書を最初から再読み込みするため、これら3つのフィールドの他のテーブルに参加する必要があります(理由は聞かないでください)。したがって、接続を維持するために、新しい請求書が更新されたデータに接続できるようにするフィールドのテーブルに参加します。
私がやろうとしているのは、グループ内のすべてのメンバーのすべての請求書を顧客ごとに(顧客名で注文)表示し、請求書を期日ごとに注文することです。
したがって、私のサイトは次のようになります。
顧客名(番号)
- 請求書1の情報
- 請求書2の情報
- 請求書3の情報
次の顧客(番号)
- 請求書A情報
- 請求書B情報
- 請求書C情報
だから、私は質問をしました
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の問題が発生します。クエリを機能させる方法について何かアイデアはありますか?
理想的には、クエリは、顧客が請求書を持っているグループID(カウント> 0)で行われ、次に顧客名と請求書の期日で並べ替えられます。それはすべて私には簡単に思えます-私は最初のセットを取得した後、順序付けと排他的処理を行っています。ただし、それでもn+1の問題が発生します。
プロファイラーでは、顧客から請求書への参加を行っていることがわかります。ただし、その後、各請求書の詳細を取得します。
これは、コードでモデルをデータモデル(nhibernateにマップされている)からビューモデル(nhibernateにマップされていない)に変換して、データベースにコールバックしないようにデータを分離しようとしているためだと思います。
データセット(foreach customer 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から分離され、データベースへのn+1呼び出しを実行する必要がないと想定したためです。しかし、それでもそれらの呼び出しを行うことができます。