How do I run an HqlBasedQuery that returns an unmapped list of objects using nHibernate? - nhibernate

I want to run a query against two tables (which happen to be mapped in ActiveRecord). The query returns a result list that cannot be mapped to an ActiveRecord object (as it is custom aggregate information).
For instance
Dim query_str as string = "Select distinct d.ID, (select count(1) as exp from Sales_Leads where date_created <= :todays_date) as NbrLeads from Dealer d"
Dim q As Queries.HqlBasedQuery = New Queries.HqlBasedQuery(GetType(ICollection), query_str)
q.SetParameter("todays_date", DateTime.Today)
Dim i As ICollection = ActiveRecordMediator.ExecuteQuery(q)
What I'm looking for is simple execution of SQL, without an ActiveRecord object returned.
So, ideally, I'd be able to look at i("NbrResults") for each item in the collection.
The error I am getting is:
You have accessed an ActiveRecord
class that wasn't properly
initialized. The only explanation is
that the call to
ActiveRecordStarter.Initialize()
didn't include
System.Collections.ICollection class

Well, this was asked a long time ago but I have a working answer.
public static IList<T> ExecuteQuery<T>(HqlBasedQuery query)
where T : new()
{
query.SetResultTransformer(new NHibernate.Transform.AliasToBeanResultTransformer(typeof(T)));
var results = (ArrayList)ActiveRecordMediator.ExecuteQuery(query);
List<T> list = new List<T>(results.Count);
for (int i = 0; i < results.Count; i++)
{
list.Add((T)results[i]);
}
return list;
}
This will give you back results of type T. Type T can be anything you want. Type T needs a no argument constructor and it needs public fields or properties that match the column names or aliases in the query you build.
We do this all the time. Particularly when you want to use aggregate function in HQL to produce aggregate data.
A companion function will allow you to just pass in your query as a string as well as any positional parameters you might have:
public static IList<T> ExecuteQuery<T, U>(string hqlQuery, params object[] parameters)
where T : new()
{
return ExecuteQuery<T>(new HqlBasedQuery(typeof(U), hqlQuery, parameters));
}
Type U is any type that is a valid ActiveRecord type. It doesn't even have to be one of the types you are referencing. If you want you could replace it will some type you know is gonna be valid int he session and ditch the extra parameter.

Here was my final solution:
Dim query_str As String = "SELECT DISTINCT d.ID, count( l ) from LEAD as l join l.Dealer as d where l.DateCreated >= DATEADD(day, -30, :todays_date) GROUP BY d.ID"
Then obtain the active record session (or NHibernate, still don't even know what is returned here):
Dim sess As ISession = activerecordmediator.GetSessionFactoryHolder().CreateSession(GetType(ActiveRecordBase))
Dim q As NHibernate.IQuery = sess.CreateQuery(query_str)
q.SetParameter("todays_date", DateTime.Today)
Dim i As IList = q.List() ' get results
In the .aspx page, the result can be accessed in a GridView like so:

You're stepping outside the NHibernate paradigm to call down to SQL, which is somewhat against the spirit of ORM. It's not 'bad' per-se, but I'd certainly avoid breaking the abstraction if I could to try to maintain a looser coupling.
You can do what you want with a straight HQL query, which will return a collection of tuples containing the results of your query. I explained how to do this here
Custom query with Castle ActiveRecord
which you might want to have a look at. Even though you must specify a type when you new an HQLBasedQuery, NH is smart enough to know that if you don't select a type instance it should assemble a result-set based on object tuples.
(IMNSHO it's still a bit non-pure - I'd be tempted to try to model this relationship as an object and map it accordingly, but then I'd have to bash the DB into shape to suit the object model and that's not going to fly in all cases.)

Related

Entity Framework: Check if value is nothing without reading all the data?

I am looking for a way to test a db field via an EF query simply to see if it is null or not, but without reading the entirety of the data. If it's not null, it's going to be huge and there are many rows, so doing this the standard way (as in the snippet below) is a very heavy operation on the db and takes ages. What I would like is to just let the user know which rows have some data in that field, and then they can choose to view it, which will then perform a single operation on that row to return just that data.
I feel like this should be possible, testing to see if something has a value or not doesn't require reading all of the data, so is there a way to achieve the desired result?
Class MyClassModel
Property HasData As Boolean?
Function Query (DB as DBContext) As IEnumerable(Of MyClassLineModel)
Dim data As IQueryable(Of MyClass) = DB.MyClasses
If HasData.HasValue Then data = From t In data Where t.Data IsNot Nothing = HasData
Dim ret = (From t In data Select New MyClassLineModel With {
.HasData = t.Data IsNot Nothing}).ToArray
Return ret
End Function
End Class
Class MyClassLineModel
Property HasData As Boolean?
End Class
p.s. This is an MVC project, hence the "model" etc. But this question is still applicable in general.
EDIT
NVM, turns out that EF does this for me. The above code converts to a db query
CASE WHEN Data IS NULL THEN 0 ELSE 1 END
which doesn't read the whole field. Response time is fine. Mods can delete the Q, or keep it for anyone else who wants to know. Thanks for all the answers guys.
You can use .Any() - if you use this on a IQueryable backed by SQL it will do something like a WHERE EXISTS in SQL which won't read all the data...
i.e.
var dataExists = _dataContext.Entity.Any(e => e.Field != NULL);

Statement lambdas cannot be converted to expression trees in Workflow Foundation 4.0

I've been pulling my hair out on this for awhile. I have a custom activity that accepts an InArgument like such:
[RequiredArgument]
public InArgument<Func<IDataReader, T>> Projection { get; set; }
And later on I use this Projection in a Select statement on a data reader after an SQL query like so:
results = reader.Select<T>(context.GetValue(this.Projection)).ToList();
Now, the only difficult part is actually passing the func variable in the VB expression. Specifically in this case, T is a list of RawTie objects. But every time I try to do something complicated with the expression to assemble a list from the reader, I get the error: Statement Lambda Functions are not supported in this context.
Here is an example of an expression I attempted. Keeping in mind VB is not my primary language:
Function(r As IDataReader) As New List(Of RawTie)
Dim ties As New List(Of RawTie)()
Do While r.Read()
'Do something with the ties
Loop
r.Close()
Return ties
End Function
Is there any information on, at least, whether this is a VB problem or a WWF problem? Just ask if you need more information.

Unable to pull fields from within LINQ query

Quick overview:
Visual Basic 2010 WinForm app pulls data from DB2. The app allows users to filter the data.
The problem:
I'm doing something wrong with my LINQ query (or object definition), as I'm not able to access fields within the dataset. Pulling data from DB2 is fine, I get that data and store it as an IEnumerable.
I plan to run this app as disconnected since its readonly for 95% of the users and it accesses 100,000+ records. Because of this, I have two datasets: 1) 'data' which is the full dataset pulled from DB2 (I don't plan on doing any modifications to it), (2) 'filteredData' which is a subset of data based on user entered filters.
Dim data As IEnumerable
Dim dataFiltered = From record in data
Select record
'Filter data based on version
Select case uxCodeVersion.Text
Case "10"
dataFiltered = From rec in dataFiltered
Where rec.
... (other parts of case statement removed)
End Select
and this is my problem. I'm expecting to see the list of fields within 'rec.' (such as rec.CodeVersion) ;however, I just receive object methods (Equals, GetHashCode, GetType, ReferenceEquals, ToString).
What simple thing am I missing?
Performance is an issue too, but I figured one problem at a time...
Thank you,
Brian.
Here is the answer as provided below.
When defining data, I need to define it to the generic list DTO. So in my case, that becomes:
Dim data As IEnumerable(Of DataAccessLayer.DiagnosisAndDiagnosisIndustryCombinedDTO)
Then when accessing the code, its the same as before, though I temporarily took out the dataFiltered field and just used data.
dataFiltered = From rec in data
Where rec.CodeVersion = uxCodeVersion.Text
From your code sample, you define data as IEnumerable. From your comment you say that your data layer returns a List(of T),
While the assignement of List(of T) to IEnumerable is valid, the type contained in the IEnumerable is Object. That's why you don't get intellisense.
You should either declare IEnumerable(of T), or do something like:
Dim data = datalayer.GetFoo()
This will cause type inference and you'll get intellisense.
Until you assign a type to IEnumerable, you won't be able to access the fields in this way. For example (c#): IEnumerable<YourType> should work.

Restricting an NHibernate query using ICriteria according to an enumeration of enums

I have an entity, with a field of type enum, that is persisted as an integer in my database.
When retrieving objects from the database using ICriteria, I wish to restrict the results to those with the field being a member of a collection of enum values. Does Restrictions.In work with a collection of enums?
The following does not work. Do I have to perform something like type-casting at the "restrictions.in" part of the query?
var myEnumCollection = new MyEnum[] { MyEnum.One };
return FindAll<MyType>(Restrictions.In("EnumProperty", myEnumCollection));
FindAll is a method encapsulating
criteria.GetExecutableCriteria(Session).List<MyType>()
My initial guess would be that you'll need to compare against the integer values of the enum members (assuming that you're mapping the enum as an integer); so something like:
var myEnumCollection = new int[] { MyEnum.One };
return FindAll<MyType>(Restrictions.In("EnumProperty", myEnumCollection));
May be the solution that you're after. If you update your post with further details (mapping of the enum member and the sql being generated by the query), I may be able to provide further assistance.

How to make a return type for a result set in LINQ

I am having a problem determining how c# and LINQ solve the common problem of handling a data structure that does not necessarily return a table structure, but instead a resultset.
I have a stored procedure that works, and have included it in my DBML
[Function(Name="dbo.p_GetObject")]
public int p_GetObject([Parameter(Name="ObjectType", DbType="NVarChar(200)")] string objectType, [Parameter(Name="ItemState", DbType="Bit")] System.Nullable<bool> itemState, [Parameter(Name="IsPublished", DbType="Bit")] System.Nullable<bool> isPublished)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), objectType, itemState, isPublished);
return ((int)(result.ReturnValue));
}
The dbml says that the return type is (None) and this could be the crux issue. However I don't have a DBML object that matches the resultset.
The SP takes three parameters, and returns a result set with three columns (ID, Name, Value) with multple rows. I can create a data object for this, and call it resultSet
When I write a function call for this, I get stuck:
public List<resultset> GetObject(string objectType, bool itemState, bool isPublished)
{
MyDataContext.p_GetObject(objectType, itemState, isPublished);
}
My questions are:
how do I have the data context call to the stored procedure populate my resultSet object? Is there a better approach? What should the return type be? A SQL view? Looking for good suggestions...
If it simply isn't understanding your SP, that could be the SET FMT_ONLY issue... try generating the data from a simplified version of the SP?
Normally, SPs / UDFs that don't map 1:1 with an existing entity would expose themselves in a generated type. You can rename this in the DBML file (not in the designer), but personally I wouldn't; I tend to mark the SP as private, and write my own method that projects into my own POCO type (defined for the repository):
var typed = from row in cxt.SomeFunction(123)
select new MyType {Id = row.Id, Name = row.Name, ...}
The reason for this is partly for repository purity, and partly to guard against the designer's habit of re-writing the DBML in unexpected ways ;-p See here for more.