return objects that are not in the mapping file from nhibernate hql - nhibernate

the title says pretty much what I'm trying to do. I have nhibernate hql with select case
select application.SubmissionDate, count(candidates)
from Application as application group by application.SubmissionDate
I would like to have the return values from this query into an object (which is not in nhibernate mapping file) called 'CountPerDay' object
class CountPerDay {
public DateTime Date,
public int Count
}
does nHibernate has sort of build in feature /methods to do this?

You should take a look at Ad-hoc mapping for NHibernate:
string hql = #"select application.SubmissionDate as Date, count(candidates) as Count
from Application as application
group by application.SubmissionDate";
var count = session.CreateQuery(hql)
.SetResultTransformer(Transformers.AliasToBean(typeof(CountPerDay)))
.List<CountPerDay>();

Related

DbContext.DbSet.FromSql() not accepting parameters

I have a database with two tables, and wrote a relatively simple select statement that combines the data and returns me the fields I want. In my SQL developer software it executes just fine.
Now to execute it in my C# .NET Core application, I created a "fake" DbSet in my DbContext that doesn't have a proper table on the database. The Type of the DbSet is a Model that represents the resulting data structure of the select statement. I use the DbSet field to access the method FromSql() and execute the select like so:
List<ProjectSearchModel> results = _ejContext.ProjectSearch.FromSql(
#"SELECT combined.Caption, combined.Number FROM
(SELECT p.Caption, p.Number, CreateDate FROM dbo.Project AS p
UNION
SELECT j.Caption, j.Number, CreateDate FROM dbo.Job AS j) AS combined
WHERE combined.Caption LIKE '{0}%' OR combined.Number LIKE '{0}%'
ORDER BY combined.CreateDate DESC
OFFSET 0 ROWS
FETCH FIRST 30 ROWS ONLY", term)
.ToList();
The SQL does properly return data, I've tested that. But the result variable holds 0 entries after executing. In the documentation for FromSql() I found that with MS SQL Servers you have to use OFFSET 0 when using ORDER BY so that's what I did.
I have no idea what I'm doing wrong.
You need to use DbQuery<T> instead:
public DbQuery<Project> ProjectSearch { get; set; }
Then, you can issue arbitrary queries using FromSql on that. With DbSet, the query must be composable, which means it can only be a standard select, it must correspond to a real table, and must include all properties on the entity - none of which apply to the query you're trying to perform.
As #ChrisPratt said, one of my mistakes was using the DbSet class instead of the DbQuery class. But also, what drove me crazy is that my parameters didn't work. My mistake was putting them inside a string, which results in them not being recognized as parameters. So my SQL string should be
...
WHERE combined.Caption LIKE {0} + '%' OR combined.Number LIKE {0} + '%'
...
instead of
...
WHERE combined.Caption LIKE '{0}%' OR combined.Number LIKE '{0}%'
...

translate hql into sql with parameters binded

our application builds hql query dynamically based on the filters provided. Now i have a use case where i need to translate the generated hql to sql and feed that to jasper reports for report generation. here is what i was able to do so far:
translate hql to sql:
private String toSql(String hql){
QueryTranslatorFactory translatorFactory = new ASTQueryTranslatorFactory();
Session session = this.es.getEntityManager().unwrap(Session.class);
SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor)session.getSessionFactory();
Map<String,Filter> filters = getEnabledFilters(session);
QueryTranslator translator = translatorFactory.createQueryTranslator(hql,hql,filters,sessionFactoryImplementor,null);
translator.compile(Collections.EMPTY_MAP,false);
return translator.getSQLString();
}
Below is an example of what i get from above method:
select person from Person person where person.id in (?);
is there a way i can get "?" binded to actual values? example: (20,30) and query should look like:
select person from Person person where person.id in (20,30);
NOTE: : I also have filters enabled on session, i need to be able to bind those values as well.

Selecting specific columns using linq: What gets transferred?

I refer to this example: Return selected specified columns
Quote:
If BlobDetails isn't the LINQ entity, then you can do it directly:
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new BlobDetails {
Id = b.Id, Size = b.Size,
Signature = b.Signature, RowVersion = b.RowVersion};
return qry.ToList();
I see that they are selecting specific column in a query through the ORM-tool LINQ TO SQL.
Critics of ORM-tools say that, if I remember correctly, that ORM-tools select and return entire objects from the table, and limits the options of selecting only specific columns as one can do through classic SQL-programming. Of course, I have my doubts about that when I see this example, but nevertheless, I still keep asking myself the question: Does the database return only the selected columns, or does it return the entire objects, leaving the column-filtering to the ORM-tool?
From this example, they also have a class called Blobdetails:
public class BlobDetails
{
public int Id { get; set; }
public string Signature { get; set; }
public int Size { get; set; }
public System.Data.Linq.Binary RowVersion { get; set; }
}
Do I need to create my own classes everytime I only wish to select a few columns from a table through LINQ?
You don't need to create new classes to select few columns from a table. You can use anonymous types for that.
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new { b.Id, b.Size, b.Signature, b.RowVersion};
return qry.ToList();
Only selected columns are transferred. There is no difference between using plain SQL and using LINQ to SQL. When you are executing LINQ query, it is converted to plain SQL and executed. Then result is mapped to your objects.
You can use SQL Server Profiler to see what query was generated and executed on server. Also you can use LINQPad to see what SQL will be generated from your query. In your case query will be same either you use BlobDetails or anonymous object:
SELECT [t0].[Id], [t0].[Size], [t0].[Signature], [t0].[RowVersion]
FROM [Blobs] AS [t0]
ORDER BY [t0].[RowVersion] DESC
when you do projections LINQ does indeed only select those columns and there is nothing preventing you from materializing it however you want. So in your example code
select new BlobDetails
{
Id = b.Id,
Size = b.Size,
Signature = b.Signature,
RowVersion = b.RowVersion
};
Only b.id, b.size, b.signature, & b.rowversion are selected. You can verify this with sql profiler or your debugger, I seem to recall there is also a function you can call on the datacontext to get the last query that was ran.
I think that the answer to your first question is already in the POST you mentioned. However...
If your BlobDetails is not LINQ entity you can simply use it in your select statement to define (shrink) your projection attributes. For example:
var qry = from b in dc.Blobs
select new BlobDetails { Id = b.Id, Size = b.Size }
would compile to SQL query like SELECT Id, Size FROM Blob ....
But if BlobDetails is LINQ entity you will need to use that AsEnumerable() hack otherwise you will get NotSupportedException: Explicit construction of entity type in query is not allowed.
var qry = from b in dc.Blobs.AsEnumerable()
select new BlobDetails { Id = b.Id, Size = b.Size }
Edit
As #Chris Pitman stated in his comment this AsEnumerable() approach could create serious bottleneck, beacause the whole table would be loaded in memory before applying the projection. So it is not recommended!
To your second question:
You will need to create custom class for objects that you want use easily outside the scope of the method. Properties of an anonymous object are visible only in the scope, where they have been declared and anonymous objects can be cast only to type object.
So if you want to return anonymous objects from method the return type would has to be an enumerable of object or dynamic as #xeondev stated in his comment.
There's no need to create your own classes, you can return an anonymous type. You can write something like this
var qry = from b in dc.Blobs
orderby b.RowVersion descending
select new {
Id = b.Id, Size = b.Size,
Signature = b.Signature, RowVersion = b.RowVersion};
return qry.ToList();
Although the signature of the method should look to something like this
public IEnumerable<object> GetItems()
or
public dynamic GetItems()
So if you are going to use the result of linq query in outer scope like you example suggest, it is highly recommended you create your own classes.

Linq to sql - get value from db function and not directly from the db field (while mapping properties)

When you map a table to an object, every property created corresponds to one db column.
I want to execute a db function on a column before it gets mapped to the property, so the property gets the value returned by the db function, and not the column
I was trying to achieve that by Expression property of ColumnAttribute (as in the example below), so instead of BirthDate the usrFn_UTCToLocalTime(BirthDate) is returned
but it does not seem to be working and still gets pure value of the column.
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_BirthDate", DbType = "DateTime", UpdateCheck = UpdateCheck.Never, Expression = "dbo.usrFn_UTCToLocalTime(BirthDate)")]
public System.Nullable<System.DateTime> BirthDate
{
get
{
return this._BirthDate;
}
}
I have also modified the DBML XML as in:
other post on stackoverflow
but also without result.
Is that possible by using LINQ or do I have to overwrite a getter which costs roundtrip to the server?
According to the Remarks section on this MSDN page, the Expression property of the ColumnAttribute is used when calling CreateDatabase, so it won't work the way you intend unless you created your database with Linq to Sql.
You can create a Linq query that selects the various columns and calls the db function in one statement like this (based on MSDN example):
var qry = from person in db.Persons
select new {
FirstName = person.FirstName,
LastName = person.LastName,
BirthDate = person.BirthDate,
Dob = db.usrFn_UTCToLocalTime(person.BirthDate)
};
This projects into an anonymous type, but you could use a non-anonymous type as well. For the sake of the example, I've got a table named Person with FirstName, LastName, and BirthDate columns, and a user defined scalar function named usrFn_UTCToLocalTime. The sql statement sent to the server is:
SELECT [t0].[FirstName], [t0].[LastName], [t0].[BirthDate], CONVERT(DateTime,[dbo].[usrFn_UTCToLocalTime]([t0].[BirthDate])) AS [Dob]
FROM [dbo].[Person] AS [t0]
As I was suggesting in the question, for now I have overwritten the get method so I have:
get
{
using (var context = DB.Data.DataContextFactory.CreateContext())
{
return context.usrFn_UTCToLocalTime(_BirthDate);
}
//return this._BirthDate;
}
But with every access to the property, roundtrip is made - which is not my intention but gives a proper result.
I leave the question still open

Determine field by formula on Insert in NHibernate

I am using NHibernate 2.1. I am inserting records into a table successfully, but I have a SortOrder column that needs to be calculated on insert. The formula will roughly be:
SortOrder = (SELECT (MAX(SortOrder) + 1) FROM MyTable WHERE CategoryID = #CategoryID)
How can I accomplish this in NHibernate on inserts? Normally I use Stored Procedures and I would just include this there.
Thanks for any help!
I am not aware of a way of doing what you ask through the mapping files. I do not think there is one.
How I would approach the problem is to plug in a PreInsertListener and do the select statement you provide in your question there to retrieve the value for the SortOrder answer just before the entity is saved.
Here is how it would roughly look:
public class NHibernateEventListeners : IPreInsertEventListener
{
public bool OnPreInsert(PreInsertEvent auditEvent)
{
var audit = auditEvent.Entity as YourEntityTypeHere;
if (audit == null)
return false;
int sortOrderValue =
(int)auditEvent.Session.CreateCriteria<YourEntityTypeHere>()
.SetProjection(Projections.Max("SortOrder"))
.Add(Restrictions.Eq("CategoryID", CatID)).UniqueResult();
SetValue(auditEvent.Persister, auditEvent.State, "SortOrder", sortOrderValue + 1);
audit.DateCreated = sortOrderValue + 1;
return false;
}
}
You can still use stored procs with nhibernate. You need to implement <sql-insert>, <sql-update> and <sql-delete> in the xml mapping file to do any specialized CRUD. I think that will be your best bet given your scenario.
In your class mapping file, you can specify a SQL formula for a column / property. Using XML mapping files:
<property name="SortOrder" formula=" (SELECT (MAX(SortOrder) + 1) FROM MyTable WHERE CategoryID = #CategoryID)"/>
Or if you are using fluent mapping:
Map(x => x.SortOrder).FormulaIs(" (SELECT (MAX(SortOrder) + 1) FROM MyTable WHERE CategoryID = #CategoryID)");
See if that works.
You could write a trigger in your database but be aware of the problems with this approach as NHibernate won't be aware of the changes in this calculated field. You have to use Flush() method after saving and Refresh(your_data_object) method after it.
There is a good detailed explanation about dealing with triggers in "NHibernate in Action" book: "9.2.4 Working with triggers".