NHibernate QueryOver distinct - nhibernate

I have this
scenario:
class User
{
Id,
UserName
}
class UserRelationship
{
User GroupUser,
User MemberUser
}
and query
var query = QueryOver.Of<UserRelationship>()
.JoinqueryOver(x=>x.MemberUser)
.Where(x=>x.UserName == "TestUser");
Now I want to return List Distinct User, so I cannot do
TransformUsing(Transformers.DistinctRootEntity)
because this will give me the UserRelationship.
I need something like this:
Select distinct user.ID
from UserRelationship relationship
inner join User user on user.ID = relationship.MemberUser_ID
Please help
thanks

Given the classes:
public class User
{
public virtual int Id {get; set;}
public virtual string UserName {get; set;}
}
public class UserRelationship
{
public virtual int Id {get; set;}
public virtual User GroupUser {get; set;}
public virtual User MemberUser {get; set;}
}
And the fluent mappings of:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x=>x.Id).GeneratedBy.Native();
Map(x=>x.UserName);
}
}
public class UserRelationshipMap : ClassMap<UserRelationship>
{
public UserRelationshipMap(){
Id(x=>x.Id).GeneratedBy.Native();
References(x=>x.GroupUser);
References(x=>x.MemberUser);
}
}
You want to retrieve a list of distinct "User" based on "MemberUser" from the UserRelationship class.
var distinctMemberUsers = QueryOver.Of<UserRelationship>()
.Select(x => x.MemberUser.Id);
var users = QueryOver.Of<User>()
.WithSubquery.WhereProperty(x=>x.Id).In(distinctMemberUsers)
This should use a In clause in the SQL to give you a distinct list of User.

I know this post is old but I just came across the same problem and thought I would share an answer I found to be much simpler.
No matter what - NHibernate will have to query multiple rows for each parent object (unless you use a SubSelect instead of a Join). Because of this, we know we're going to get a list of say, 500 objects, when there are really only 100 unique objects.
Since these objects are already queried, and already in memory - why not use LINQ?
Based on this question: LINQ's Distinct() on a particular property the answer with the most +'s gives a very eloquent solution. Create another list, and have LINQ do the distinct comparison. If we could do distinct at the database it would clearly be the better option - but since that's not an option, LINQ seems to be a good solution.

Related

RavenDB Spatial Filter

Help with Understanding RavenDB query, I'm using following model to store & index spatial data.
public class GeoRecord {
public string SearchId {get; set;}
public string Tag {get; set;}
public double Latitude {get; set;}
public double Longitude {get; set;}
}
public class GeoRecord_GeoIndex : AbstractIndexCreationTask<GeoRecord>
{
public GeoRecord_GeoIndex()
{
Map = records => from #record in records
select new
{
IndexName = "geoIndex",
record.SearchId,
Coordinates = CreateSpatialField(#record.Latitude, #record.Longitude)
};
}
}
I can able to filter all GeoRecord's using Spatial query like below:
await session
.Query<GeoRecord, GeoRecord_GeoIndex>()
.Spatial("Coordinates", factory => factory.Within(shapeWkt: wkt))
.ToListAsync();
However I would like to filter by both SearchId & Coordinates, I got this solution however I would like to understand if it uses GeoRecord_GeoIndex rather than filtering the results from GeoRecord.
await session.Advanced.AsyncDocumentQuery<GeoRecord, GeoRecord_GeoIndex>()
.WhereIn("SearchId", activeSearchIds)
.Intersect()
.Spatial("Coordinates", criteria => criteria.Within(shapeWkt:wkt))
.ToListAsync();
You are querying index GeoRecord_GeoIndex, and it is the one used to filter.
A static index contains:
A list of indexed terms for each index-field specified in the Map function,
(your index-fields are: IndexName, SearchId & Coordinates)
A mapping to the relevant document
At query time, the indexed terms are filtered according to your query and the relevant documents are fetched from the database
some link to demo:
https://demo.ravendb.net/demos/csharp/static-indexes/map-index#step-3

How to auto-load details (with conditions) associated with an entity using Ria Services?

I'm developing a project using Silverlight 4 and Entity Framework 4 and I'm trying to auto-load the details (with conditions) associated with an entity when the client loads the EntityQuery.
So far, I've been able to put in place a solution, using the Include attribute, that returns all the details associated with the master entity. What I'm missing here is to be able to filter out the details based on some criteria.
As an example, here's what my entities look like:
Entity Movie
Id (int)
[Include]
MovieLocalizedInformations (EntityCollection<MovieLocalizedInformation>)
Entity MovieLocalizedInformation
Id (int)
Movie_Id (int)
LanguageCode (eg.: en)
Title
On my DomainService object, I expose the following method:
public IQueryable<Movie> GetMovies( string languageCode )
{
return this.ObjectContext.Movies.Include( "MovieLocalizedInformations" );
}
This works fine. But when I try to add where clause to filter out the localized information based on the language code, only the movies get loaded on the client.
Is there a way to achieve the filtering in one query?
Note: I'm also using the DomainDataSource with paging on the client so the solution needs to work with that.
Any help would be greatly appreciated!
Thanks,
Jacques.
Not sure about Enitity Framework but with a LinqToSqlDomainService you use the LoadWith loadOption
to include the details entities and then use the AssociateWith LoadOption to filter the detail e.g
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Movies>(i => i.MovieLocalizedInformations);
options.AssociateWith<Movies>(i => i.MovieLocalizedInformations.Where(d=> myListOfIds.Contains(d.LocationId)));
Ok,
For efficiency reason, I decided to go with custom DTO object that fetches the localized information and flatten the result.
But, the same problem occurred when my custom DTO needed to reference another custom localized DTO.
Here is how I came to do the same as the .Include( "PropertyName" ) that the ObjectSet offers:
Entity LocalizedMovieCollection
public class LocalizedMovieCollection
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
[Include]
[Association( "LocalizedMovieCollection_LocalizedMovies", "Id", "MovieCollection_Id" )]
public IEnumerable<LocalizedMovie> Movies { get; set; }
}
Entity LocalizedMovie
public class LocalizedMovie
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
public int MovieCollection_Id { get; set; }
[Include]
[Association( "LocalizedMovie_LocalizedMovieCollection", "MovieCollection_Id", "Id", IsForeignKey = true]
public LocalizedMovieCollection MovieCollection { get; set; }
}
Then, I've declared two methods: One that returns an IQueryable of LocalizedMovieCollection and the other, an IQueryable of LocalizedMovie. (Note: There must be at least one method that returns each type of entity for the entity to get auto-generated on the Silverlight client)
My goal is to automatically load the MovieCollection associated with a Movie so the method definition to get the movies is as follow:
public IQueryable<LocalizedMovie> GetMovies( string languageCode )
{
return from movie in this.ObjectContext.Movies
join movieLocalizedInfo in this.ObjectContext.MovieLocalizedInformations
on movie equals movieLocalizedInfo.Movie
join movieCollection in this.ObjectContext.MovieCollections
on movie.MovieCollection equals movieCollection
join movieCollectionLocalizedInfo in this.ObjectContext.MovieCollectionLocalizedInformations
on movieCollection equals movieCollectionLocalizedInfo.MovieCollection
where movieLocalizedInfo.LanguageCode == languageCode && movieCollectionLocalizedInfo.LanguageCode == languageCode
select new LocalizedMovie()
{
Id = movie.Id,
Name = movieLocalizedInfo.Name
MovieCollection_Id = movieCollection.Id,
MovieCollection = new LocalizedMovieCollection(){ Id = movieCollection.Id, Name = movieCollectionLocalizedInfo.Name }
}
}
When the Silverlight client loads the query, all the LocalizedMovies and their associated LocalizedMovieCollections will be loaded into the context.

Mapping interface on multiple hierarchies with NHibernate

Given 2 classes that are not related, one of which is a member of
another inheritance hierarchy, how can I map an interface on both of
the classes so that I can query against the interface and have the
appropriate concrete type returned?
E.g.
public abstract class Survey
{
public Guid Id { get; private set; }
}
public class InviteOnlySurvey : Survey
{
public ICollection<Invite> Invites { get; private set; }
}
public class Invite : ISurveyGateway
{
public Guid Id { get; private set; }
public InviteOnlySurvey Survey { get; private set; }
}
public class SharedSurvey : Survey, ISurveyGateway { ... }
public interface ISurveyGateway
{
Guid Id { get; }
}
Currently I have mapped Survey, InviteOnlyLiveSurvey and SharedLiveSurvey using table per class hierarchy and now I am trying to figure out how to map ISurveyGateway so that I can query against it and have NHibernate find the matching entity ( Invite or
SharedLiveSurvey ) seamlessly. ISurveyGateway instances are effectively readonly as all the remaining persistence concerns are managed through the mappings for SharedSurvey and Invite.
If I remove the ISurveyGateway interface from either SharedSurvey or Invite, I can query and retrieve ISurveyGateway instances via NHibernate, but as soon as I apply the interface to 2 different hierarchies I get an exception with the message "Ambiguous persister for ISurveyGateway implemented by more than one hierarchy" (which is expected - I just don't know how to make it work).
The answer with QueryOver and FutureValue works, but here is an even simpler solution:
public ISurveyGateway FindSurveyGatewayById( Guid id )
{
var surveyGateway = session
.QueryOver<ISurveyGateway>
.Where( s => s.Id == id )
.SingleOrDefault<ISurveyGateway>();
return surveyGateway;
}
But you should be careful, your Id should be a Guid. If it's not the case you may get multiple responses...
Given that both Invite and SharedSurvey are already mapped and being used and because the Ids are Guids which ensures that there will not be a SharedSurvey and an Invite with the same Id ( with a fairly high degree of certainty ) I found a far simpler approach.
public ISurveyGateway FindSurveyGatewayById( Guid id )
{
var sharedSurveyGateway = session.QueryOver<SharedSurvey>
.Where( s => s.Id == id )
.FutureValue<ISurveyGateway>();
var inviteGateway = session.QueryOver<Invite>
.Where( i => i.Id == id )
.FutureValue<ISurveyGateway>();
return sharedSurveyGateway.Value ?? inviteGateway.Value;
}
There are some downsides with this - the main one being that this query now has to be extended for every ISurveyGateway that is added to the system, but for now it does the job with the least complexity and with reasonable performance.

NHibernate - How to write this Query: Select parents & find child for each parent that matches a condition

OK, first my simple Domain Model is 2 classes with a one-to-many relationship, a simple Parent -> child relationship. A 'Tweet' has one or more 'Votes', but each Vote belongs to just one Tweets etc.
public class Tweet
{
public virtual long Id { get; set; }
public virtual string Username { get; set; }
public virtual string Message { get; set; }
public virtual ISet<Vote> Votes { get; set; }
}
public class Vote
{
public virtual long Id { get; set; }
public virtual long TwitterUserId { get; set; }
public virtual DateTime VotedDate { get; set; }
public virtual Tweet Tweet { get; set; }
}
I'm trying to write a query in either HQL, ICriteria or NHibernate LINQ, that selects all Tweets, but also add two columns that selects:
A count of the number of votes, and...
Whether a particular user has voted for that tweet, based on a particular TwitterUserId
With the two extra columns, I'm not expecting to get Tweet domain object back, and would probably need to run a Report query, that's OK. But I'm struggling to figure out how to write this query.
I know how to write this as a Stored Procedure, or using LINQ 2 SQL. If it helps I will express this as a LINQ to SQL query.
long userId = 123;
var tweets = from t in dataContext.Tweets
where t.Application == app
orderby t.PostedDate desc
select new TweetReport()
{
Id = t.Id,
Username = t.Username,
Message = t.Message,
TotalVotes = t.Votes.Count(),
HasVoted = t.Votes.Any(v => v.TwitterUserId == userId)
};
I know this will work in LINQ 2 SQL, and generate reasonably efficient T-SQL, but I can't figure out how to write this in NHibernate.
Update: I tried running the above LINQ query in NHibernate by using the NHibernate LINQ provider built for NHibernate v2, eg:
var tweets = from t in Session.Linq<Tweet>()
where (snip)
But it didn't work. Has LINQ support in Nhibernate 3.0 improved? I'm a bit reluctant to use version 3.0 because it's still alpha, but if this will work, then I might give it a go.
Update 2: Thanks to Diego Mijelshon's suggestion, I upgraded to NHibernate 3.0 alpha 2 and wrote the query in LINQ:
var tweets = from t in Session.Query<Tweet>()
where t.App == app
orderby t.PostedDate descending
select t;
int totalRecords = tweets.Count();
var pagedTweets = (from t in tweets
select new TweetReport()
{
Id = t.Id,
TwitterId = t.TweetId,
Username = t.Username,
ProfileImageUrl = t.ImageUrl,
Message = t.Message,
DatePosted = t.PostedDate,
DeviceName = t.Device.Name,
DeviceUrl = t.Device.Url,
TotalVotes = t.Votes.Count(),
HasVoted = t.Votes.Any(v => v.TwitterUserId == userId)
})
.Skip(startIndex)
.Take(recordsPerPage)
.ToList();
return new PagedList<TweetReport>(pagedTweets,
recordsPerPage, pageNumber, totalRecords);
It's exactly the same with NHibernate 3; just replace dataContext.Tweets with session.Query<Tweet>.
Alternatively, you can create a context class that exposes session.Query<Tweet> as an IQueryable<Tweet> Tweets property, then the code would be 100% unchanged.

NHibernate Projection Components

Hello guys im trying to hydrate a DTO using projections in NHibernate this is my code
IList<PatientListViewModel> list =
y.CreateCriteria<Patient>()
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("Birthdate"), "Birthdate")
.Add(Projections.Property("Doctor.Id"), "DoctorId")
.Add(Projections.Property("Gender"), "Gender")
.Add(Projections.Property("Id"), "PatientId")
.Add(Projections.Property("Patient.Name.Fullname"), "Fullname")
)
.SetResultTransformer(Transformers.AliasToBean<PatientListViewModel>())
.List<PatientListViewModel>();
this code is throwing an exception? anyone know what is the problem?
here is the error message
Message: could not resolve property: Patient.Name.Fullname of: OneCare.Domain.Entities.Patient
You have to create a join to your Parent.Name property.
So try before setting the projections to create in alias to your Patient.Name property
e.q.
IList<PatientListViewModel> list =
y.CreateCriteria<Patient>()
.CreateAlias("Name", "name")
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("Birthdate"), "Birthdate")
.Add(Projections.Property("Doctor.Id"), "DoctorId")
.Add(Projections.Property("Gender"), "Gender")
.Add(Projections.Property("Id"), "PatientId")
.Add(Projections.Property("name.Fullname"), "Fullname")
)
Sorry I did not check this, as all depend on your entities classes. But the idea is that you have to create an alias.
If you can not fix the issue, please provide the your classes.
Updated!
I've created two entities, Patient and Doctor:
public class Patient : AdvanceEntity
{
public virtual DateTime BirthDate { get; set; }
public virtual Doctor Doctor { get; set; }
public virtual int Gender { get; set; }
public virtual string Name { get; set; }
}
public class Doctor : AdvanceEntity
{
public virtual string Name { get; set; }
}
Next the repository contains only yours query translated to Criteria API
public IList<Patient> GetPatientsForDoctor(long doctorId)
{
return this.Session.CreateCriteria(typeof(Patient), "patient")
.CreateAlias("patient.Doctor", "doc")
.Add(Restrictions.Eq("doc.Id", doctorId))
.List<Patient>()
;
}
And here is the unittest and the result of the query
[Test]
public void CanGetPatients()
{
var repository = new PatientRepository();
repository.GetPatientsForDoctor(1L);
}
and result is:
NHibernate: SELECT this_.patientId as patientId70_1_, this_.birthDate as birthDate70_1_, this_.gender as gender70_1_,
this_.name as name70_1_, this_.deletedDate as deletedD5_70_1_, this_.doctorId as doctorId70_1_,
this_.deletedById as deletedB7_70_1_, doc1_.doctorId as doctorId71_0_, doc1_.name as name71_0_,
doc1_.deletedDate as deletedD3_71_0_, doc1_.deletedById as deletedB4_71_0_
FROM Patients this_
inner join Doctors doc1_ on this_.doctorId=doc1_.doctorId
WHERE doc1_.doctorId = #p0;#p0 = 1
As I said you need just to create an Alias and join tables between them.
But I think, that using HQL is more plausible in this case. Use criteria only you have dynamic queries. As you can see, the criteria one select all fields which could create a performance lack. Of couse you are working with simple things, but in real application be very carefully with generated queries.
Have a nice day!