How to use a inner join in Fluent NHibernate to fetch a single property from the right table - nhibernate

I have a query that fetches every property from one table, however the query should fetch one additional property from another table.
I'm using a simple example where querying every property from the second table wouldn't have any affect in performance, however the real situation does have many properties.
This is the queried table model:
public class Car
{
public long Id;
public long SellerId;
public string Name;
public string SellerName;
}
However, in the SellerName property is not being mapped, since this property is in another model:
public class Seller
{
public long Id;
public string SellerName;
}
The Car class mapping is currently as follow:
Property(x => x.Id);
Property(x => x.SellerId);
Property(x => x.Name);
This will create a model like this one:
Car Table
|------------------------------------------------|
|Id | SellerId | Name |
|1 | 1 | Car Number one |
|------------------------------------------------|
Employee Table
|---------------------------|
|Id | Name |
|1 | John Doe |
|---------------------------|
Using a simple query I can easily use an inner join to gather the Car data with the related employee name:
select c.*, p.Name from Car c
inner join Employee e on c.Id = e.EmployeeId
I've already tried this query:
var query = from car in session.Query<Car>()
join employee in session.Query<Employee>() on car.SellerId equals employee.Id
select new Car
{
Id = car.Id,
SellerId = car.SellerId,
Name = car.Name,
ClientName = employee.ClientName
};
But this actually creates two queries in the database, one for the Client table and another one for the Employee one. And this isn't using Linq, which is also a requirement.

First point here is, you have to use virtual properties for NHibernate's behaviour. You could map the relation on your model, for sample
public class Car
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual long SellerId { get; set; }
public virtual Employee Seller { get; set; }
}
And your map,
public class CarMap : ClassMap<Car>
{
public CarMap()
{
// map other properties...
// You could map the the SellerId to be easy to access here (just readonly, use the reference to persist)
Map(x => x.SellerId).Column("SellerId").Not.Nullable().ReadOnly();
References(x => x.Seller).Column("SellerId");
}
}
Given you have a right mapped model and you are using Linq, you could use Fetch method from NHibernate.Linq namespace and it will force a join for you avoiding the lazy loading. For sample:
var data = session.Query<Car>()
.Fetch(x => x.Seller)
.ToList();

Related

NHibernate join 2 tables and project to DTO with collection

I am trying to join 2 tables, and project directly to DTOs (NHibernate 5).
I have following entities:
public class Person {
public Guid Id {get;set;}
public string Name {get;set;}
}
public class Car {
public Guid Id {get;set;}
public string Brand {get;set;}
public Person Owner {get;set;}
}
as we see, there is just reference from Car to Person (car knows its owner), and this is ok in my whole project.
However, there is one place, where I need to query all Persons, and make each person with collection of owned cars.
I created such DTOs:
public class PersonDto {
public Guid Id {get;set;}
public string Name {get;set;}
public IList<CarDto> {get;set;}
}
public class CarDto {
public Guid Id {get;set;}
public string Brand {get;set;}
}
it is kind of present the data linked together upside-down.
This task seems trivial using SQL or LINQ (GroupJoin) however I found it extremly hard to do in NH, since GroupJoin is not implemented in NH.
Can you please help me how to solve above issue?
Just add a Car collection to the existing Person entity and mark the collection inverse. Collections in NHibernate are lazy by default, so when you query Person, it won't read the cars from the DB, until you start iterating them. In other words adding the Car collection won't affect the way your code works.
When you want to efficiently query persons together with cars, force NH to do a join
.QueryOver<Person>.Fetch(person => person.Cars).Eager
I'd like to answer my own question. Thanks Rafal Rutkowski for his input.
To get data that has association in only 1 direction, we need to introduce a new data model, and then, manually convert result to our entities. I hope following example is best possible answer:
1) create such data model to store NH response:
private class PersonCarModelDto
{
public Guid PersonId { get; set; }
public string PersonName { get; set; }
public Guid CarId { get; set; }
public string CarBrand { get; set; }
}
2) create such a model to store hierarchical data as output:
private class PersonModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public IList<CarModel> Cars { get; set; }
}
private class CarModel
{
public Guid Id { get; set; }
public string Brand { get; set; }
}
3) now the NH query:
Person personAlias = null;
Car carAlias = null;
PersonCarModelDto resultAlias = null;
var response = GetCurrentSession().QueryOver<Car>(() => carAlias) // notice we are going from from 'downside' entity to 'up'
.Right.JoinAlias(c => c.Owner, () => personAlias) // notice Right join here, to have also Persons without any car
.SelectList(list => list
.Select(() => personAlias.Id).WithAlias(() => resultAlias.PersonId)
.Select(() => personAlias.Name).WithAlias(() => resultAlias.PersonName)
.Select(() => carAlias.Id).WithAlias(() => resultAlias.CarId)
.Select(() => carAlias.Brand).WithAlias(() => resultAlias.CarBrand)
.TransformUsing(Transformers.AliasToBean<PersonCarModelDto>())
.List<PersonCarModelDto>()
;
4) now we have flat data as a list of PersonCarModelDto, but we want to make output model:
var modelResult = response.GroupBy(p => p.PersonId)
.Select(x => new PersonModel
{
Id = x.Key,
Name = x.Select(y => y.PersonName).First(), // First() because each PersonName in that group is the same
Cars = x.Select(y => new CarModel
{
Id = y.CarId,
Name = y.CarBrand
})
.ToList()
})
.ToList()
;
Conclusions:
the problem is much easier to solve having bidirectional associations
if for some reason, you don't want bidirectional associations, use this technique
this approach is also useful if your entities has a lot of other properties, but for some reason you need just a small part of them
(optimiziation of data returned for DB)

Not sure how to join tables using Fluent NHibernate

I'm using NHibernate on legacy tables and after looking through our codebase, I seem to be the only person with this need. I need to join two tables so I can run a query, but I haven't made any progress today. I'll try to abbreviate where it makes sense in my code snippets. Care to help?
Tables--
Order
OrderID (primary key)
OrderName
OrderType
OrderLocation
OrderAppendix
ID (composite key)
Key (composite key)
Value (composite key)
The ID portion of the OrderAppendix composite key is associated with OrderID in the Order table; therefore, and Order can have several entries in the OrderAppendix table.
Domain Objects--
[Serializable]
public class Order
{
public virtual string OrderID { get; set; }
...
public virtual string OrderLocation { get; set; }
}
[Serializable]
public class OrderAppendix
{
public virtual string ID { get; set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
public override bool Equals(object obj)
{
...
}
public override int GetHashCode()
{
...
}
}
Mapping
internal sealed class OrderMap : ClassMap<Order>
{
Table("Order");
Id(x => x.OrderID).Column("OrderID").Length(20).GeneratedBy.Assigned();
Map( x => x.OrderName).Column("OrderName")
....
}
internal sealed class OrderAppendixMap : ClassMap<OrderAppendix>
{
Table("OrderAppendix");
CompositeId()
.KeyProperty(x => x.ID, "ID")
....
Map( x => x.ID).Column("ID);
...
}
I won't muddy this up with my futile attempts at joining these tables, but what I would like to do is query by things like OrderType or OrderLocation given that the results all have the same Value from the OrderAppendix table.
Example desired SQL
SELECT * FROM ORDER
INNER JOIN
ON Order.OrderID = OrderAppendix.ID
WHERE OrderAppendix.Key = "Stuff"
Edit
Here's where I've gotten by reading the documentation on "QueryOver"
http://nhibernate.info/doc/nh/en/index.html#queryqueryover
Order Order = null;
OrderAppendix OrderAppendix = null;
resultList = session.QueryOver<Order>(() => Order)
.JoinAlias(() => Order.OrderAppendix, () => OrderAppendix)
.Where(() => OrderAppendix.Key == "MatchThis")
.List();
I think I'm on the right track using aliases to join the table, but obviously I haven't found a way to inform NHibernate of the many-to-one mapping that's needed. Also, you can see that I've added a property of type OrderAppendix to Order in order to the use the alias functionality.
Well, I didn't get why you did not used standard parent-child approach using HasMany on Order side and Reference of Appendix. This is because of composite key on appendix side?
If you would do that, you will be able to provide following HQL:
SELECT o FROM Order o
LEFT JOIN FETCH o.apxs a
WHERE a.Key="Stuff"

(Fluent) NHibernate: Map field from separate table into object

I'm currently trying to get (Fluent)NHibernate to map an object to our legacy database schema. There are three tables involved.
Table a contains most information of the actual object I need to retrieve
Table b is a table which connects table a with table c
Table c has one additional field I need for the object
An SQL query to retrieve the information looks like this:
SELECT z.ID, z.ZANR, e.TDTEXT
FROM PUB.table_a z
JOIN PUB.table_b t ON (t.TDKEY = 602)
JOIN PUB.table_c e ON (e.ID = t.ID AND e.TDNR = z.ZANR)
WHERE z.ZANR = 1;
The main problem is how to specify these two join conditions in the mapping.
Entity for table a looks like this:
public class EntityA
{
public virtual long Id { get; set; }
public virtual int Number { get; set; }
public virtual string Name { get; set; }
}
Name should map to the column table_c.TDTEXT.
The mapping I have so far is this:
public class EntityAMap : ClassMap<EntityA>
{
public EntityAMap()
{
Table("PUB.table_a");
Id(x => x.Id).Column("ID");
Map(x => x.Number).Column("ZANR");
}
}
I tried to map the first join with the same strategy as in How to join table in fluent nhibernate, however this will not work, because I do not have a direct reference from table_a to table_b, the only thing connecting them is the constant number 602 (see SQL-query above).
I didn't find a way to specify that constant in the mapping somehow.
you could map the name as readonly property easily
public EntityAMap()
{
Table("PUB.table_a");
Id(x => x.Id).Column("ID");
// if ZANR always has to be 1
Where("ZANR = 1");
Map(x => x.Number).Column("ZANR");
Map(x => x.Name).Formula("(SELECT c.TDTEXT FROM PUB.table_b b JOIN PUB.table_c c ON (c.ID = b.ID AND b.TDKEY = 602 AND c.TDNR = ZANR))");
}
Update: i could only imagine a hidden reference and the name property delegating to there
public EntityAMap()
{
HasOne(Reveal.Member<EntityA>("hiddenEntityC"));
}
public class EntityB
{
public virtual int Id { get; set; }
public virtual EntityA EntityA { get; set; }
}
public EntityCMap()
{
Where("Id = (SELECT b.Id FROM PUB.table_b b WHERE b.TDKEY = 602)");
References(x => x.EntityA).PropertyRef(x => x.Number);
}

Mapping One to One in FluentNHibernate, NHibernate tries to write child before parent

I'm trying to do a 1:1 relationship in FN but its being a bit of a pain.
I looked at http://avinashsing.sunkur.com/2011/09/29/how-to-do-a-one-to-one-mapping-in-fluent-nhibernate/ which seemed to confirm this should work but hey ho, it keeps trying to insert records into the child tables before the parent which means the child-inserts dont contain the CustomerId which is required as its a foreign key constraint.
Tables
+-----------------------+ +----------------------+
| tblCustomer | | tblCustomerPhoto |
|-----------------------| |----------------------|
| | 1:1 | |
| CustomerID (PK) |+---->| CustomerID (FK) |
| OtherJunk... | | Photo (Image) |
| | | |
| | | |
+-----------------------+ +----------------------+
Models
public class Customer
{
public virtual int CustomerID { get; private set; }
/* public virtual Other stuff */
}
public class CustomerPhoto
{
public virtual int CustomerID { get;set;}
public virtual Byte[] Photograph { get; set; }
}
Maps
public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
public CustomerPhotoMap()
{
Id(x => x.CustomerID)
.Column("CustomerID")
.GeneratedBy.Assigned();
Map(x => x.Photograph)
.CustomSqlType("Image")
.CustomType<Byte[]>()
.Nullable();
}
}
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID)
.GeneratedBy.Identity()
.Column("CustomerID");
HasOne<CustomerPhoto>(x => x.CustomerPhoto)
.LazyLoad()
.ForeignKey("CustomerID");
}
}
Test
class CustomerMappingFixture : IntegrationTestBase
{
[Test]
public void CanMapCustomer()
{
new PersistenceSpecification<Customer>(Session)
.CheckReference(x => x.CustomerPhoto, new CustomerPhoto(){ Photograph = Arrange.GetBitmapData(ImageFormat.Jpeg) })
.VerifyTheMappings();
}
}
Now the foreign key column on the CustomerPhoto was set to not-null and I was repeating this in the notation on the CustomerPhotoMapping.
On the basis of ( https://stackoverflow.com/a/2286491/529120 ) I changed that to nullable and removed the notation from the mapping.
Regardless of which, NHibernate returns
System.NullReferenceException : Object reference not set to an instance of an object.
and appears to be trying to insert a CustomerPhoto record first, passing zero as the CustomerId; then creating the Customer record, then trying to select the customer and photo using a left outer join. Which obviously wont work as at no point has it attempted to update the ID in the photo table.
Few things I noticed
Using CheckReference to verify this mapping is probably incorrect. I'm pretty sure this is only for many-to-one relationships. Therefore it makes sense that it's trying to insert the CustomerPhoto before the Customer. I would write a test using straight up NH sessions here. I found it to be more trouble than it's worth to use PersistenceSpecification for many of my mappings that were non-trivial.
The one to one mappings looked like they were a bit off (proposed solution below)
When mapping an image column to a byte array I don't think there is a need to declare a custom type. The mapping below has worked fine for me. I think the other stuff you have on that property mapping is un-needed.
I have a mapping almost identical to yours and this is what I use:
public class Customer
{
public virtual int CustomerID { get; private set; }
public virtual CustomerPhoto CustomerPhoto { get; set; }
/* public virtual Other crap */
}
public class CustomerPhoto
{
public virtual Customer Customer { get; set; }
public virtual Byte[] Photograph { get; set; }
}
public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
public CustomerPhotoMap()
{
Id(x => x.Id)
.Column("CustomerID")
.GeneratedBy.Foreign("Customer");
Map(x => x.Photograph).Length(Int32.MaxValue);
}
}
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerID).GeneratedBy.Identity()
.Column("CustomerID");
HasOne(x => x.CustomerPhoto)
.Cascade.All();
}
}

Nhibernate ManyToMany with a dynamic where clause

I'm having some issues mapping a complex many to many relationship in fluentnhibernate. I have a legacy db which looks something like this:
Foos: | Id | Foo |
FooBars: | FooId | BarId |
Bars: | Id | Bar | CultureId |
which I am trying to map to the following object model:
class Foo
{
property virtual int Id { get; set; }
property virtual string Foo { get; set; }
property virtual IList<Bar> Bars { get; set; }
}
class Bar
{
property virtual int Id { get; set; }
property virtual int CultureId { get; set; }
}
with the mappings:
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("foos");
Id(v => v.Id);
Map(v => v.Foo);
HasManyToMany(v => v.Bars)
.Table("FooBars")
.ParentKeyColumn("FooId")
.ChildKeyColumn("BarId")
.Cascade.All();
}
}
public class BarMapping : ClassMap<Bar>
{
public BarMapping()
{
Table("bars");
Id(v => v.Id);
Map(v => v.Bar);
Map(v => v.CultureId);
}
}
The problem is I have multiple Bar's with the same Id for different CultureIds
e.g.
I would have a table that looks like:
Id|Bar|CultureId
1, Hello, 1
1, Bonjour, 2
1, Gutentag, 3
At the moment, the Bars property for the above table will return 3 elements but the Bar property on it will return "Hello" for all three elements (presumably because they all have the same identity). So my question is, how can I either stop this happening or can anyone suggest a way of filtering rows that do not have the correct culture id (note, this is dynamic & based on the current culture)?
You can't create dynamic where clauses in your mappings. You're going to need to query this collection instead of accessing it via the parent, using a Criteria or HQL query. You could read up on filters, but they still require a query.