NHibernate expand composite id from HQL - nhibernate

We have a table with a composite primary key mapped, as shown below:
mapping.CompositeId().KeyReference(e => e.Node).KeyProperty(e => e.DtFr).UnsavedValue("any");
We need to delete several rows from the table by complex condition, for example n.Node.Contract.Code = "1234"
According to the solution we tried next HQL:
delete from Entity n where n in (select c from Entity c where c.Node.Contract.Code = "1234")
But in the generated SQL-query there are no parentheses around the primary key columns:
select ... from ENTITY n where n.IDNODE, n.DTFR in (select c.IDNODE, c.DTFR ...
We tried explicitly to add parentheses in HQL (where (n) in), but NHibernate ignores them.
What can we do to make NHibernate generate the correct SQL?
Version of NHibernate is 4.1.1.4000, RDBMS is Oracle 10.2.
SQL dialect is set to NHibernate.Dialect.Oracle10gDialect.

It is very likely there is a bug in NHibernate. Issue is added: https://github.com/nhibernate/nhibernate-core/issues/1376.
So for now the only solution is: load all the entities you want to delete and then delete them.
var entities2delete = session.CreateQuery("select c from Entity c where c.Node.Contract.Code = \"1234\"").List();
foreach (var item in entities2delete) session.Delete(item);

Related

EF Core The LINQ expression 'DbSet() could not be translated

What am I missing here?
IQueryable<IsActiveCustomerProviderRefDto> search =
from customerProviderRef in _database.CustomerProviderRef
join customerTrade in _database.CustomerTrade on customerProviderRef.ExternalRefId equals customerTrade.CustomerProviderRef into ctJoin
from customerTradeJoin in ctJoin.DefaultIfEmpty()
select new IsActiveCustomerProviderRefDto
{
CustomerProviderRef = customerProviderRef.ExternalRefId
};
search = search.Where(e => e.CustomerId.Equals(find.customerProviderRef.CustomerId));
return search.FirstOrDefault();
Generates the following error:
Description:The LINQ expression 'DbSet() .Where(c => new IsActiveCustomerProviderRefDto{ CustomerProviderRef = c.ExternalRefId } .CustomerId.Equals("1014706150563885056"))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.(Source: Microsoft.EntityFrameworkCore)
I've tried replacing the FirstOrDefault() call with ToList(), etc but makes no difference.
I'm not entirely sure if my query is spot on either, so I'll provide a SQL version of the query I was attempting to achieve (LINQ is not my specialty)...
SELECT *
FROM CustomerProviderRef [R]
LEFT OUTER JOIN [CustomerTrade] [T]
ON [R].[ExternalRefId] = [T].[CustomerProviderRef]
WHERE
[T].[Id] IS NULL -- essentially where the left outer join fails.
AND
[R].[CustomerId] = #CustomerId
That query looks a bit off, in that you are doing the Where clause after the projection (Select) which I don't believe would compile. The Where clause would expect to be limited to just the values that were selected.
The main issue will be based on what "find" represents in your example. Linking IQueryable queries in EF is possible when EF is happy they can be translated into a single SQL statement, but generally you should avoid it if at all possible. Manually joining DbSets should be reserved for relationships which cannot be represented in the database with actual FK constraints. These are de-normalized relationships, usually hacks to "save space" but result in slow, buggy querying. An example would be something like an Address table with an OwnerType and OwnerId Where OwnerType might be "Customer" or "Business" or several other values, used to define whether the OwnerId should pair to the CustomerId or BusinessId etc. These are slow and buggy because they cannot benefit from FKs or constraints for performance or data integrity.
EF and Linq should not be viewed as a like-for-like replacement for SQL using explicit joins between DbSets. Instead, set up the relationships between entities using Navigation properties with 1-to-many, many-to-one, many-to-many, or 1-to-1 relationships as the database schema structure reflects through FK references. This is what EF is designed to do, to generate SQL to pull back the applicable data from a object model representation of that related data.
based on the query with the relationship (1-to-many between CustomerProviderRef and CustomerTrade on ExternalRefId your Linq query would look more like:
var results = dbContxt.CustomerProviderRef
.Where(x => x.CustomerId == customerId
&& !x.CustomerTrades.Any())
.ToList();
This assumes that there are multiple CustomerProviderRef per CustomerID and you want the ones that have no Trades.
If there is only one expected CustomerProviderRef for a given customer you want to load it and determine if it has any trades:
var customerProviderRef = dbContxt.CustomerProviderRef
.Include(x => x.CustomerTrades)
.Where(x => x.CustomerId == customerId)
.Single();
bool hasTrades = customerProviderRef.CustomerTrades.Any();
This eager loads the trades with the customer, which will fetch any trades when we load this customer's data. EF will work out the joins as neded. From there we can check if the customer has any trades, and add Trades to that CustomerTrades collection and call SaveChanges() on the DbContext and EF will create the row and associate the FKs automatically.

SQL "not in" syntax and nested SELECT in Entity Framework

I recently used Entity Framework. For elementary CRUD operations, I have no problems, but for more complicated queries, I do not know how to do these.
For example: how to write a nested select? How to use the NOT IN operator?
The query that I want to write is:
SELECT *
FROM course
WHERE idCourse NOT IN (SELECT idCourse
FROM reservation
WHERE idStudent = 'value');
I do not know where to start. Could you please give me some advice?
If I do not misunderstood your question, you want to know how to write the question's query as LINQ-to-Entity query.
Examples could be:
var courses = context.Courses
.Where(c => c.Reservations.All(r => r.idStudent != "value"))
.Select(c => c);
// with eager loading
var courses = (from c in context.Courses.Include(c => c.Reservations)
where c.Reservations.All(r => r.idStudent != "value")
select c).ToArray();
var courses = (from c in context.Courses
join r in context.Reservations on c.idCourse equals r.idCourse
where r => r.idStudent != "value"
select c).ToArray();
The Contains() is equivalent to EXIST IN in a query. Well, 1st and 2nd nearly the same. Only difference is the Include method to show how you could eager load data.
In the 3rd query I use the join key word to do a join operation - equivalent to an INNER JOIN. The result will contain only records where a relation between a course and a reservation exists and the searched student ID is referenced in the reservation.
If you should not use LINQ for your querys, you should take a look. It's a perfect way to decouple your data access layer from persistence layer and you could test all your queries in code too.
Here you could get a very good entry into the subject.
EDIT:
Modified example code to fit NOT IN.
If you are having trouble executing complex queries with LINQ, you can also execute raw SQL queries. They are easy and faster than linq queries.
Here is an example in c#
var list = dc.Database.SqlQuery<YourCourceClass>(#"SELECT *
FROM course
WHERE idCourse NOT IN(SELECT idCourse
FROM reservation
WHERE idStudent = 'value');").ToList();

How to select one table using NHibernate CreateCriteria

How can I create the following SQL statement in Nhibernate using CreateCriteria:
SELECT distinct top 20 a.* from ActivityLog a
left join WallPost w on a.ActivityLogId = w.ActivityLogId left join ItemStatus i on i.StatusId = w.ItemStatus
I always tend to get all columns from all tables returned in the sql statement producing duplicates even though I map it to the ActivityLog table. I am also doing paging as the code below shows:
ICriteria crit = nhelper.NHibernateSession.CreateCriteria(typeof(Model.ActivityLog), "a").CreateAlias("a.WallPosts", "w",CriteriaSpecification.LeftJoin)
.CreateAlias("w.ItemStatus", "i", CriteriaSpecification.LeftJoin)
.SetMaxResults(pageSize).SetFirstResult(startRow).AddOrder(Order.Desc("a.Date"));
Thanks
H
Sounds like you have set lazy loading to false in your mapping files meaning that all the associations and child collections are being loaded up too. Can you verify that?
You ask "How to select one table using NHibernate CreateQuery" (HQL). In that case you can choose what to fetch using select.
In your text you're using criteria. AFAIK, you cannot directly control what columns you want to read. However, if you create a DetachedCriteria for the join, this won't be fetched.

How write hibernate criteria for specific SQL?

i need write similar SQL by ICriteria:
Tables:
1Lvl -- 2Lvl -- 3Lvl
SQL:
SELECT * FROM 2Lvl
WHERE 2Lvl.1LvlFK in
(
SELECT 1Lvl.Id
FROM 3Lvl
JOIN 2Lvl ON 3Lvl.2LvlFK = 2Lvl.Id
JOIN 1Lvl ON 2Lvl.1LvlFK = 1Lvl.Id
WHERE 3Lvl.Id = 123
)
I'm sorry for so specific question, but I inherited project with Hibernate from exemployee and I can't still understand hibernate-criteria.
var subQuery = DetachedCriteria.For<Lvl3>("lvl3")
.CreateAlias("Lvl2", "sublvl2", JoinType.InnerJoin)
.CreateAlias("Lvl1", "lvl1", JoinType.InnerJoin)
.Add(Restrictions.EqProperty("sublvl2.Id", "lvl2.Id")
.Add(Restrictions.Eq("lvl3.Id", 123)
.SetProjection(Projections.Property("lvl1.Id"));
Session.CreateCriteria<Lvl2>("lvl2")
.Add(Subqueries.PropertyIn("Lvl1.Id", subQuery));
Should do the trick. I've made assumptions about what your entities are called since 1Lvl, etc.. are not valid C# identifiers. I've also assumed the primary key column of each table was Id. Also a note that this won't produce the exact SQL you're looking for, but it will get you the same result.

NHibernate : Count childrens' children

I have an entity type A. Which has many B's. The B entity has many C's.
I need to count how many C's does an A entity have. How can this be done using NHibernate Criteria API?
Using LINQ to NHibernate I was unable to get results since it throws an exception (see this question)
This query becomes simpler if you use C as starting point of the query, rather than A. This is possible since you use bidirectional mappings, according to the mapping you show in your other question.
The way I would do this is to find all Cs which have a B which has a given A, and then count the found Cs.
To Add a constraint on the B of a C, you can add an alias and then add restrictions to that alias. To perform a count query rather than returning the found Cs you can use the SetProjection method and specify a Count projection. Since the count projection returns a single integer value, use UniqueResult to get the count.
using (ISession session = SessionFactorySingleton.OpenSession())
{
int numberOfCsForA1 = session.CreateCriteria<C>()
.SetProjection(Projections.Count("Id"))
.CreateAlias("B", "b")
.Add(Restrictions.Eq("b.A.Id", a1.Id))
.UniqueResult<int>();
// ...
}
The SQL generated by this query looks like this:
SELECT count(this_.Id) as y0_
FROM [C] this_
inner join [B] b1_
on this_.IdB=b1_.Id
WHERE b1_.IdA = #p0;#p0 = 12
As you can see, it is a two-way join, since NHibernate is smart enough to realize that it does not need to join with the A table to get the id of a Bs A. Instead it simply looks at the IdA value of the B.