NHibernate: ProjectionList: Can we create dynamic projectionlist for Orderby - nhibernate

I am working on a query in NHibernate in which user could provide a sorting order for some selected fields. I need to do a OrderBy() in QueryOver with the names of field names in entities, but when using projection list I am getting like this.
SELECT
this_.Number as y0_,
scc3_.Code as y1_,
FROM sometable
WHERE 1=1 ORDER BY this_.Number as y0_,
scc3_.Code as y1_ asc
The projection list for select columns is different from orderby projection list and I am not using .WithAlias()
sortProjectionList.Add(Projections.Property(() => stock.Number));
sortProjectionList.Add(Projections.Property(() => scc.Code));
How can I create a projectionlist without aliases or aliases with custom names?
Thanks

I expect, that you experience an issue with generated keyword "AS" inside of the ORDER BY in case, that you pass projections like this:
// NHibernate will leave few AS, except the last
query.OrderBy(sortProjectionList).Asc();
To avoid that, we can do it like this:
// NHibernate will solve each projection separately
for (var index = 0; index < sortProjectionList.Length; index++)
{
query.OrderBy(sortProjectionList[index]).Asc();
}

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.

Nhibernate and a large list of ids in a WHERE in restriction

I am perfomring a QueryOver query, and have a restriction cluase that effectively does a WHERE IN cluase.
If I will have thousands of results coming back from that inner query, this will obviously be a slow query right?
public List<SomeEntity> GetByIds(List<Guid> listOfIds)
{
return NHibernateHelper.Session.QueryOver<SomeEntity>()
.WhereRestrictionOn(x => x.id).IsIn(listOfIds)
.List();
}
Is it possible to convert this into a INNER JOIN somehow?
You can convert it to a sub-query or inner join. Your restriction is on the number of parameters you can add to a query in sql server, which is about 2.2 k parameters. The way your current code works you should split the parameters in chunks from about 2k and add them to a result list.
if listOfIds comes from the same database then only when there is a realtionship, otherwise you have to use sql.
if listOfIds is large and doesnt come from the same source then this might be faster
Session.CreateSqlQuery("CREATE TEMP TABLE temp (Id)").ExecuteUpdate();
foreach (var id in listofids)
{
Session.CreateSqlQuery("INSERT INTO temp (?)").SetParam(id).ExecuteUpdate();
}
Session.CreateSqlQuery("CREATE INDEX for temp (Id)").ExecuteUpdate();
return NHibernateHelper.Session.QueryOver<SomeEntity>()
.UnderlyingCriteria.Add(Expression.Sql("0 < (SELECT Count(*) FROM temp t WHERE t.id = Id)"))
.List<SomeEntity>();

In continue of questions "DB Schema for storing tagged records" -- how to select list of items with tags?

Good day!
There are a lot of questions how to store tags in DB here on stackoverflow, I finally decide to use Toxi approach (items table, tags table and many-to-many intermediate table) http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html
I want to display a list of 20-50 tagged items on a page each with list of it's tags (I use LINQ2SQL in ASP.NET 3.5).
I see some options:
First query for list of items than run query for each item to select tags for each item -- seems to be slow, but caching on application level can improve situation.
Denormalize items table and store list of tags for item in text field in "items" table.
However both options seem bad to me, may be I'm missing something?
Thanks in advance!
I don't recommend Option #2 because it will overcomplicate attempts to isolate a tag or group of tags. My recommendation would be to leave the data model as is (ITEMS, TAGS, ITEM_TAGS_XREF), and query against a VIEW which would contain a computed column containing the list of tags. If you mentioned what database you're using, we can provide the query that will construct a comma/etc delimited string of the tags an item is associated with.
Assuming primary and foreign keys are setup accordingly, the performance shouldn't be bad - is it possible the lag is occurring in the application layer?
If I understood correctly ...
Maybe you could create a query that joins all three table. In only one fast query, you would get all information... In SQL, it could be something like:
select ... //itemColumns, tagColumns
from item
join tagItem on item.id = tagItem.itemId
join tag on tag.id = tagItem.tagId
where ... // your other conditions
2KLE: thanks, I got it. The result of this query is one row for each tags (Item data is duplicated), so anyway there is need to process resultset in application code. Am I right?
For LINQ 2 SQL I used DataLoadOptions to specify what associated data I want to load. Here is an example of code generated by LINQ2SQL to select all items with all tags.
Please note that Items table is called Snippets in my example (so we have Snippets, Tags and SnippetsTags tables). Another thing to note is that LINQ2SQL doesn't support Many-To-Many relations out of the box, so there is an entity class for intermediate table (SnippetsTag). Here are C# code:
using (SnippetsDataContext context = UtilsLinq.CreateContext())
{
DataLoadOptions dl = new DataLoadOptions();
dl.LoadWith<Snippet>(s => s.SnippetsTags);
dl.LoadWith<SnippetsTag>(st => st.Tag);
context.LoadOptions = dl;
var result = (from s in context.Snippets
select s).ToList();
string x = result.First().SnippetsTags.First().Tag.Title;
}
Here is a SQL which LINQ to SQL generates:
SELECT [t0].[Id], [t0].[Title], [t0].[Text], [t0].[Created], [t1].[Id] AS [Id2], [t1].[TagId], [t1].[SnippetId], [t2].[Id] AS [Id3], [t2].[Title] AS [Title2], (
SELECT COUNT(*)
FROM [dbo].[SnippetsTags] AS [t3]
INNER JOIN [dbo].[Tags] AS [t4] ON [t4].[Id] = [t3].[TagId]
WHERE [t3].[SnippetId] = [t0].[Id]
) AS [value]
FROM [dbo].[Snippets] AS [t0]
LEFT OUTER JOIN ([dbo].[SnippetsTags] AS [t1]
INNER JOIN [dbo].[Tags] AS [t2] ON [t2].[Id] = [t1].[TagId]) ON [t1].[SnippetId] = [t0].[Id]
ORDER BY [t0].[Id], [t1].[Id], [t2].[Id]