3

顧客に参加する必要があります。アドレスがプライマリである場合、PrimaryAddress1 および PrimaryCity フィールドに入力します。顧客マッピングには既に住所クラスとの関係がたくさんありますが、すべての住所を取得したくありません (パフォーマンスの問題)。

助けてください..

クラス:

public class Customer
{
    public Customer()
    {
        Addressess = new List<Address>();
    }
   public virtual int CustomerID { get; set; }
    public virtual int? BranchID { get; set; }
    public virtual int? CustTypeID { get; set; }
    public virtual string CompanyName { get; set; }
    public virtual string Prefix { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string MiddleName { get; set; }
    public virtual string LastName { get; set; }
    public virtual string PrimaryAddress1 { get; set; }
    public virtual string PrimaryCity { get; set; }
    public virtual List<Address> Addresses { get; set; }
}


public class Address
{
    public Address()
    {
    }
    public virtual int LocationID { get; set; }
    public virtual int? CustomerID { get; set; }
    public virtual string LocationName { get; set; }
    public virtual string Address1 { get; set; }
    public virtual string Address2 { get; set; }
    public virtual string Address3 { get; set; }
    public virtual string City { get; set; }
    public virtual bool Primary { get; set; }
}

マッピング:

public TblCustomerMap()
{
    Table("tblCustomers");
    LazyLoad();
    Id(x => x.CustomerID).GeneratedBy.Identity().Column("CustomerID");
    Map(x => x.ProfileID).Column("ProfileID");
    Map(x => x.BranchID).Column("BranchID");
    Map(x => x.DateEntered).Column("DateEntered");
    Map(x => x.DateTerminated).Column("DateTerminated");
    Map(x => x.CustTypeID).Column("CustTypeID");
    Map(x => x.CompanyName).Column("CompanyName").Not.Nullable().Length(50);
    Map(x => x.Prefix).Column("Prefix").Not.Nullable().Length(50);
    Map(x => x.FirstName).Column("FirstName").Not.Nullable().Length(50);
    Map(x => x.MiddleName).Column("MiddleName").Not.Nullable().Length(50);
    Map(x => x.LastName).Column("LastName").Not.Nullable().Length(50);
    HasMany(x => x.Address).KeyColumn("CustomerID");
    Map(x => x.PrimaryAddress1).Column("PrimaryAddress1") // from table tbladdress where address is primary and get data from address1 column
    Map(x => x.PrimaryCity).Column("PrimaryCity") // from table tbladdress where address is primary and get data from city column
}

クエリ:

 var query = session
            .QueryOver<Customer>(() => customer)
            .JoinQueryOver(() => customer.Addresses, () => address)
            .Where(() => address.Primary)
            .List();

        foreach (var customer1 in query)
        {
            customer1.PrimaryAddress1 = customer1.Addresses[0].Address1;
            customer1.PrimaryCity = customer1.Addresses[0].City;
            customer1.PrimaryState = customer1.Addresses[0].StateOrProvince;

        }

新しいクエリ:

  var query = session.Query<Customer>()
            .SelectMany(c => c.Addresses,
                        (c, a) => new {c, a})
            .Where(cust => cust.a.Primary)
            .Select(item => new CustomerView()
                              {
                                 CustomerID = item.c.CustomerID,
                                 CompanyName=  item.c.CompanyName,
                                 FirstName=  item.c.FirstName,
                                  LastName=item.c.LastName,
                                  Address1=item.a.Address1,
                                  Address2=item.a.Address2,
                                 Address3= item.a.Address3,
                                 City= item.a.City,
                                 StateOrProvince= item.a.StateOrProvince
                              });
        return query.ToList();
4

2 に答える 2

3

これを実現するには、(少なくとも) 2 つの方法があります。

1) より直感的で読みやすいのは、DB を拡張し、NHibernateマッピングを調整することです。新しいビュー viewPrimaryAddressを作成する必要があります。

SELECT ***columns*** FROM [tbladdress] WHERE Primary = true

顧客マッピングは次のようになります。

Join("[viewPrimaryAddress]", 
{
  m.Fetch.Join();
  m.KeyColumn("CustomerID");
  m.Map(t => t.PrimaryAddress1).Column("PrimaryAddress1");
  m.Map(t => t.PrimaryCity).Column("PrimaryCity");
});

以上です。1 つの SQL ステートメントが発行されるため、Address コレクションをロードする必要はありません。

2) 第二のアプローチの概要

2 番目のアプローチは、新しいクラス マッピングによってそのビューを作成します。これは少し複雑ですが、アプリケーション側 (C# および NHiberante) でのみ実行できます。

新しいクラスPrimaryAddressが作成され、クラス レベルの定義にフィルターが含まれます (xml マッピングの例:

<class name="PrimaryAddress" ... where="Primary = true" >

Customer次に、 への新しい多対 1 の関係で拡張できPrimaryAddressます。したがって、列「PrimaryAddress1」および「PrimaryCity」のプロパティの取得は、WHERE 句でフィルター処理された SQL 選択を介して行われます。

拡張:

次の手順では、1 対 1 のプロパティとしてプライマリ アドレスをターゲットにして、新しいマッピングを作成する方法について説明します。1) C# PrimaryAddress:

public class PrimaryAddress
{
    public virtual Customer Customer { get; set; }
    public virtual string Address { get; set; }
    public virtual string City { get; set; }
}

2) マッピング:

public TblPrimaryAddressMap()
{
    Table("tbladdress");
    LazyLoad();
    Where("Primary = 1");
    // Id as AddressId
    References(x => x.Customer).Column("CustomerID");
    Map(x => x.Address).Column("PrimaryAddress1")
    Map(x => x.PrimaryCity).Column("PrimaryCity")
}

3) のための新しいプロパティCustomer

public class Customer
{  
   ..
   public virtual PrimaryAddress PrimaryAddress { get; set; }

4) Customer public TblCustomerMap() { ... HasOne(x => x.PrimaryAddress ) の新しいマッピング

Customerこの時点で、セッションから取得したとき。アクセスできます

customer.PrimaryAddress.Address
customer.PrimaryAddress.City

私は主に XML マッピングを使用しています。しかし、これらの行から概念は明確になるはずです...fetch="join"ロードできるものCustomerPrimaryAddress、1つのSQL選択で遊ぶ

また、顧客プロパティさえ必要な場合はCustomer.PrimaryAddress1、PrimaryAddress.Address を getter でラップするだけです。

さらに得られるのは、この新しいプロパティでフィルタリングして並べ替えることができることですPrimaryAddress

: このアプローチは、キャ​​ッシングには脆弱です。その理由は、実際のAddressエンティティを変更する一方で、削除するメカニズムが構築されていないためPrimaryAddressです。また、1 つのアドレスが primary = true に設定されている Customer のみを許可するようにビジネス層に強制する必要があります。

于 2012-11-23T05:55:09.743 に答える
2

私は同様のアドレス構造を持っており、これに苦労しました。主要な懸念事項の 1 つは、Customer に主要な住所がない場合に Customer レコードが返されるようにすることです (発生しないはずですが...)。

Customer エンティティに住所フィールドを含めたい場合は、結合マッピングを参照してください。

私のアプローチは、データ転送オブジェクト (DTO またはビュー) を使用することです。この場合は CompanyDto:

    internal static QueryOver<Company> GetCompanyDtoQuery()
    {
        Address addr = null;
        CompanyDto dto = null;

        var query = QueryOver.Of<Company>()
            .Left.JoinAlias(c => c.Addresses, () => addr)
            .SelectList(list =>
                        {
                            list.Select(c => c.CompanyId).WithAlias(() => dto.CompanyId)
                            list.Select(a -> addr.AddressId).WithAlias(() => dto.AddressId
                            return list;
                        })
            .Where(a => addr.AddressId == null || addr.IsPrimaryAddress)
            .TransformUsing(new AliasToDtoDeepTransformer<CompanyDto>("PrimaryAddress"));
        return query;
    }

[編集]

クエリを使用してこれを行う方が簡単です。次に例を示します。

            var target = (from c in session.Query<Company>()
                          from a in c.Addresses
                          where a.IsPrimaryAddress
                          select new
                              {
                                  c.CompanyId,
                                  a.Address1
                              })
                .Take(10).ToList();
            Assert.IsTrue(target.Any());

投稿したクエリの問題は、コレクションにインデックスでアクセスすることにより、遅延ロードを強制していることです。熱心な読み込みを使用して、1 回の選択ですべてのアドレスを取得できます。

             var target = session.QueryOver<Company>()
                                 .Fetch(c => c.Addresses).Eager
                                 .Take(10).List();
             Assert.IsTrue(target.Any());

ただし、最初の返信で述べたように、ドメイン モデルに別のテーブルのフィールドを含めるのではなく、結合または別のアプローチを検討する必要があります。

于 2012-11-23T14:03:23.823 に答える