I need to perform a join on the customer. Where address is primary and populate PrimaryAddress1 and PrimaryCity field. Customer mapping already has hasmany relationship with address class but I do not want to fetch all addresses (performance issues).
Please help..
classes:
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; }
}
Mapping:
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
}
Query:
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;
}
New query:
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();
There are (at least) two ways how to achieve this.
1) The more intuitive and readable is extending DB and adjusting NHibernate mapping. We will need new view
viewPrimaryAddressto be created.And Customer mapping will look like this:
And that’s it. One SQL Statement will be issued, so no need to load the Address collection
2) Oveview of the second approach
The second approach will create that view by a new class mapping, is a bit complicated, but can be done only on the application side (C# & NHiberante).
New class
PrimaryAddresswill be created and contain the ilter on a class level definition (xml mapping example:Then we can extend
Customerwith new many-to-one relation toPrimaryAddress. So getting properties for column “PrimaryAddress1” and “PrimaryCity” would be done via SQL select filtered in the WHERE Clause.EXTENDED:
Next steps should guide you how to create new mapping, targeting the primary address as the one-to-one property
1) C# PrimaryAddress:
2) Mapping:
3) New property for
Customer4) New mapping for
Customerpublic TblCustomerMap()
{
…
HasOne(x => x.PrimaryAddress)
At this moment, when you get
Customerfrom session. You can accessI am working with XML mapping mostly. But from these lines the concept should be clear… Playing with some
fetch="join"you can loadCustomerand itsPrimaryAddressin one SQL selectAnd if you need even customer property
Customer.PrimaryAddress1, just wrap the PrimaryAddress.Address in the getter.What you’ll gain more, is possiblity to filter and order by over this new property
PrimaryAddressNOTE: this approach is fragile for Caching. The reason is, that whil you will change the real
Addressentity, there is no built mechanism to evictPrimaryAddress. And also, you should force the business layser to allow only Customer with one Address set to primary = true