If I want to create a DTO from properties on classes, all of which are mapped, can I do this without writing a regular SQL statement?
I've seen a lot of documentation on making the DTO class, as well as using it, but not much about how it gets created. I've seen SQL queries used with the Transformer, but that requires the use of magic strings.
Edit:
Thanks for the comments. I am aware there are multiple methods to retrieve DTOs, but I have been unable to find examples of methods that don't use SQL/HQL. Is there a reference somewhere, or is this one of those sparsely documented areas of NHibernate?
From your latest comment here are some examples of projecting a DTO using QueryOver and Linq
Using QueryOver:-
var schoolList = Session.QueryOver<lmschool>()
.SelectList(i => i
.Select(p => p.Name).WithAlias(() => dto.Name)
.Select(p => p.Lat).WithAlias(() => dto.Lat)
.Select(p => p.Lng).WithAlias(() => dto.Lng)
)
.Where(w => w.Lat != null && w.Lng != null)
.TransformUsing(Transformers.AliasToBean<MarkerDto>())
.List<MarkerDto>();
Using the NH Linq provider:-
var schoolList = (from school in Session.Query<lmschool>()
.Where(w => w.Lat != null && w.Lng != null)
select new LmSchoolMarkerDto {
Name = school.Name,
Lat = school.Lat,
Lng = school.Lng,
}).ToList();
Source is from my blog
Also if you need to see how multiple joins are used the see this blog post.
There are also lots of S.O. posts that should help you, see this search.
You could load all necessary entities and use AutoMapper to map them to DTOs more easily ?
Related
I am trying to get a QueryOver working using a Projection on a many-to-one.
The class "Post" has a property many-to-one "Creator".
Using
session.QueryOver(Of Post).
Select(Projections.
Property(of Post)(Function(x) x.Creator).
WithAlias(Function() postAlias.Creator)).
TransformUsing(Transformers.AliasToBean(Of Post)()).
List()
works BUT each creator is retrieved by a single query rather than using a join like it is done when not using a select/projection. So if there are 5 posts with 5 different creators, 6 queries will be run 1 for the list of posts and 5 for the creators.
I tried to get it working using a JoinAlias but nothing really did the job.
I already searched for a solution, but all solutions I found did use the Linq-Provider which does not really fit since the actual "field list" is passed via a parameter.
Does anyone know if there is a solution to this other than the linq provider?
There is a solution, we can use projections for many-to-one and then custom result transformer.
DISCLAIMER: I can read VB syntax but do not have enough courage to write... I expect that you can read C# and convert it into VB....
So we can have projection like this:
// aliases
Post root = null;
Creator creator = null;
// projection list
var columns = Projections.ProjectionList();
// root properties
columns.Add(Projections.Property(() => root.ID).As("ID"));
columns.Add(Projections.Property(() => root.Text).As("Text"));
// reference properties
columns.Add(Projections.Property(() => creator.ID).As("Creator.ID"));
columns.Add(Projections.Property(() => creator.FirstName).As("Creator.FirstName"));
// so our projections now do have proper ALIAS
// alias which is related to domain model
// (because "Creator.FirstName" will be use in reflection)
var query = session.QueryOver<Post>(() => root)
.JoinAlias(() => root.Creator, () => creator)
.Select(columns)
Now we would need smart Transformer, our own custome one (plugability is power of NHibernate). Here you can find one:
public class DeepTransformer
And we can continue like this
var list = query
.TransformUsing(new DeepTransformer<Post>())
.List<Post>()
Check also this:
Fluent NHibernate - ProjectionList - ICriteria is returning null values
NHibernate AliasToBean transformer associations
I am trying to find a better way to load the relation than this:
result = session.Get<Author>(id);
Course course = result.Courses.FirstOrDefault();
I can do this with QueryOver API like this:
result = session.QueryOver<Author>()
.Where(item => item.Id == id)
.Fetch(item => item.Courses).Eager
.SingleOrDefault();
I guess it would generate the same SQL but it is too verbose.
Is there a way to do something like below?
session.Fetch(result, author => author.Courses);
Get is driven by mapping. If it really make sense, change your mapping (but I would not do that). There is no runtime switch of constructed mapping.
From my experience, few more select statements during the Get(id) is not an issue... And for N + 1 you've already shown the better solution in your question.
Interesting reading about eagar loading: http://nhforge.org/wikis/howtonh/lazy-loading-eager-loading.aspx
I seem to get an error when querying an object that has a child POCO property object:
could not resolve property: PreferredLanguage.Name of: AcademyData.Entities.StudentsInformation
My query:
Session.QueryOver<StudentsInformation>().Where(x => x.Active == 1 && x.PreferredLanguage.Name == firstName).List()
If I remove the x.PreferredLanguage.Name criteria, it works, and I see the correct Name value of the object, but it won't let me use it as a query. What am I doing wrong?
Here is my mapping:
References(x => x.PreferredLanguage).Column("PreferredLanguageID");
Also, I've split my POCO's and mappings into 2 seperate assemblies, if that makes any difference. I configure the mapping so:
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<AcademyData.Dummy>().AddFromAssemblyOf<data.core.Dummy>())
Ignore the dummy, was using it to test something.
EDIT: I've tested it using an entity from the same assembly and the problem still exists.
EDIT:
I found a solution to my problem:
return SessionManager.Session.QueryOver<StudentsInformation>().JoinQueryOver<PersonBase>(s => s.Person).Where(p => p.FirstName == firstName).List();
Is this the right way of doing it, or should I still be able to do it without the JoinQueryOver for a different table?
Alternatively, you can use LINQ instead of QueryOver:
return Session.Query<StudentsInformation>()
.Where(x => x.Active == 1 && x.PreferredLanguage.Name == firstName)
.ToList()
You have to join on the child entity if you wish to query by that child entity. That is the correct way of doing it. You could also use JoinAlias if you wish.
I've got a pretty complex object graph that I want to load in one fell
swoop.
Samples have Daylogs which have Daylog Tests which have Daylog
Results
Daylog Tests have Testkeys, Daylog Results have Resultkeys, and
TestKeys have Resultkeys.
I'm using the QueryOver API and Future to run these all as one query,
and all the data that NHibernate should need to instantiate the entire
graph IS being returned, verfied by NHProf.
public static IList<Daylog> DatablockLoad(Isession sess,
ICollection<int> ids)
{
var daylogQuery = sess.QueryOver<Daylog>()
.WhereRestrictionOn(dl => dl.DaylogID).IsIn(ids.ToArray())
.Fetch(dl => dl.Tests).Eager
.TransformUsing(Transformers.DistinctRootEntity)
.Future<Daylog>();
sess.QueryOver<DaylogTest>()
.WhereRestrictionOn(dlt =>
dlt.Daylog.DaylogID).IsIn(ids.ToArray())
.Fetch(dlt => dlt.Results).Eager
.Inner.JoinQueryOver<TestKey>(dlt => dlt.TestKey)
.Fetch(dlt => dlt.TestKey).Eager
.Inner.JoinQueryOver<ResultKey>(tk => tk.Results)
.Fetch(dlt => dlt.TestKey.Results).Eager
.Future<DaylogTest>();
sess.QueryOver<DaylogResult>()
.Inner.JoinQueryOver(dlr => dlr.DaylogTest)
.WhereRestrictionOn(dlt =>
dlt.Daylog.DaylogID).IsIn(ids.ToArray())
.Fetch(dlr => dlr.ResultKey).Eager
.Fetch(dlr => dlr.History).Eager
.Future<DaylogResult>();
var daylogs = daylogQuery.ToList();
return daylogs;
}
However, I still end up with proxies to represent the relationship
between Testkey and ResultKey, even though I'm specifically loading
that relationship.
I think this entire query is probably representative of a poor
understanding of the QueryOver API, so I would like any and all advice
on it, but primarily, I'd like to understand why I get a proxy and not
a list of results when later I try to get
daylogresult.resultkey.testkey.results.
Any help?
The answer was to call NHibernateUtil.Initialize on the various objects. Simply pulling the data down does not mean that NHibernate will hydrate all the proxies.
You have to load all your entities in one QueryOver clause to get rid of proxies. But in this case you will have a lot of joins in your query, so I recommend to use lazy loading with batching.
Here is what I am trying:
IQueryable query = this.MyRepository.GetShippingCollection();
IList<SomeListType> myList = query.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
Produces this error:
could not resolve property: Settings.ID
If I do it this way it works, but causes over 3,000 queries on my SQL Server:
IList<SomeListType> myList = this.MyRepository.GetShippingCollection().ToList();
myList = myList.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
I know the solution resides within using a "Join".
I have been looking at examples for the last couple hours and can only find Join examples within the Mapping file. I am also finding examples for "ICriteria".
I don't want to have to create seporate entries for all my complex queries in the mapping file so the join within that file will not work.
Since I am using Fluent NHibernate, I am not using "ICriteria". I am using "IQueryable". How do you join multiple tables within "IQueryable"?
Any help with this would be greatly appreciated.
Thanks in advance.
If the second query is executing 3,000 queries, it is almost certainly lazy-loading the Settings collection. As you iterate over the list, you access this collection, and each time NHibernate goes back to the database to fetch it. Try setting the fetch mode for the Settings property to eager load in the mapping.
Beyond that, the LINQ provider could be an issue. What version of NHibernate are you using? The 2.x LINQ provider has some real limitations. It has been reimplemented in the 3.0 trunk, but you'll have to download and compile it from the source.
By the way, ICriteria vs IQueryable is not related to Fluent NHibernate. Criteria API and LINQ are two providers through which you can create queries. Fluent NHibernate is an alternative way to perform configuration.