I have two entities that are connected through one to many relationship.
like this example:
public class Category
{
public int Id {get; set; }
public string Name {get; set;}
}
public class Product
{
public int Id {get; set;}
public string Name {get; set;}
public Category ProductCategory {get; set;}
}
and the mapping using fluent nhibernate
public class CategoryMap : ClassMap<Category>
{
....
}
public Class ProductMap: ClassMap<Product>
{
...
References(x => x.ProductCategory).Column("CategoryId");
}
When I do search using linq to NHibernate with where clause and equal, it generates inner join between product and category
so this
var query = session.Query<Product>()
.Where (x => x.ProductCategory.Name == "abc");
this will generate inner join
but when I do search with startwith like this
var query = session.Query<Product>()
.Where (x => x.ProductCategory.Name.StartWith("abc"));
this will generate outer join between the two.
Why, and how can I force to generate inner join?
You can do it using QueryOver, which is another method to create query in NHibernate. In that case, you specify the join type as you want.
Category category = null;
var result = session.QueryOver<Product>()
.JoinAlias(x => x.ProductCategory, () => category, JoinType.InnerJoin)
.Where(Restrictions.Like(Projections.Property(() => category.Name), "abc%", MatchMode.Start))
.List();
On the other hand, query over is more verbose code, you have to specify a lot of things you avoid using linq.
Related
SELECT *
FROM HCMTemplates
INNER JOIN Status ON HCMTemplates.TemplateStatus=Status.Id
INNER JOIN Country ON HCMTemplates.AssignToCountry=Country.code
Where HCMTemplates.TemplateName like '%ABC%'
I am unable write this query in format of Entity FrameWork
Assuming the tables listed have relationships like
public class HCMTemplates
{
//properties
public ICollection<Status> Status {get; set;}
public ICollection<Country> Country {get; set;}
}
public class Status
{
//properties
public HCMTemplates HCMTemplates {get; set;}
}
public class Country
{
//properties
public HCMTemplates HCMTemplates {get; set;}
}
you could use
context.HCMTemplates.Include(x => x.Status).Include(x => x.Country).Where(x => x.TemplateName.Contains("ABC")).ToList();
if no relationship is establish
context.HCMTemplates
.Join(
dbContext.Status,
hCMTemplates=> hCMTemplates.TemplateStatus,
status=> status.Id,
(hCMTemplates, status) => new { hCMTemplates, status}
)
.Join(
dbContext.Country,
joinedTable => joinedTable.hCMTemplates.AssignToCountry,
country=> country.code,
(joinedTable , country) => new { joinedTable , country}
)
.Where(x => x.joinedTable.hCMTemplates.TemplateName.Contains("ABC")).ToList()
not sure with the Where clause, but mostly that is the code
The where statement actually doesn't really work as you would expect for entity framework. You cannot simply use a where after an include and expect it to work. Check out this article: https://entityframework.net/include-with-where-clause
Did you try this? If you did, can you show us your C# code?
I have an issue in NHibernate regarding a left join using "JoinAlias" when the result query SQL that I am looking for is this :
"select * from EntityA T1 left join EntityB T2 on T2.EntityAId=T1.id"
And in NHibernate I have this but doesn't work:
var query = _session.QueryOver(() => EntityA)
.Left.JoinAlias(() => EntityA, () => EntityB.EntityA)
In NHibernate EntityA doesn't reference to EntityB but EntityB as a reference to EntityA.
public class EntityA
{
public int Id {get;set;}
}
public class EntityB
{
public int Id {get;set;}
public EntityA EntityA {get;set;}
}
How can I make this very simple left join in HHibernate Work?
This is not possible with Criteria or QueryOver. But we can use HQL, which does support that
14.2. The from clause (small cite and snippet)
Multiple classes may appear, resulting in a cartesian product or "cross" join.
from Formula, Parameter
from Formula as form, Parameter as param
So in the case above we would have HQL like this:
FROM EntityA T1
, EntityB T2
WHEERE T2.EntityAId = T1.id
Almost the same issue
But in case described above, there is reversed relation, already mapped. And that means, that we can extend the C# entity definitions:
public class EntityA
{
public int Id {get;set;}
// one-to-many
// the bidirectional mapping of the below relation
public IList<EntityB> EntityBColl { get; set; }
}
public class EntityB
{
public int Id {get;set;}
// many-to-one
// this is the inversed end in fact of the bidirectional mapping
public EntityA EntityA {get;set;}
}
Having that in place, we can use standard QueryOver API:
// aliases
EntityA EntityA = null;
EntityB EntityB = null;
// query
var query = session.QueryOver(() => EntityA)
.Left.JoinAlias(() => EntityA.EntityBColl , () => EntityB)
...
;
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"
Consider following class structure.
public abstract class Animal
{
public string SomeData { get; set; }
public IList<AnimalProperty> AnimalProperties { get; set; }
public IList<OtherAnimalProperty> OtherAnimalProperties { get; set; }
}
public class Dog: Animal
{
public DogProperty DogProperties { get; set; }
}
With following mapping for the Dog class
HasOne(x => x.DogProperties).ForeignKey("Id");
I now have following query to retrieve a list of objects of type Animal.
list = session.QueryOver<Animal>()
.WhereRestrictionOn(e => e.SomeData).IsIn(someList.ToArray())
.Fetch(e => e.AnimalProperties).Default
.Fetch(e => e.OtherAnimalProperties).Default
.List();
My list now contains an object of type Dog (as expected), but the DogProperties property is null. Meaning, the one-to-one class defined in the mapping is not initialized.
However, NHibernate generated a nice joined query that retrieves also the DogProperty data from the database. It just does not create the class through this query.
SELECT this_.Id as Id0_1_, this_.Name as Name0_1_, this_.DogName as DogName0_1_,
this_.AnimalType as AnimalType0_1_, dogpropert2_.Id as Id1_0_,
dogpropert2_.MyProperty as MyProperty1_0_
FROM [Animal] this_ left outer join [DogProperties] dogpropert2_
on this_.Id=dogpropert2_.Id
Can anyone elaborate what I'm missing here? Or is this a bug/default behavior of NHibernate QueryOver?
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);
}