I'm new to NHibernate and not great at Linq, but this is seriously kicking my butt, and I can't find any really clear examples on SO.
I need to get Thread information from the database, but I need to include a subquery that does a count on the number of Posts on a particular thread.
Here is a SQL Statement
Select ID, ThreadName,
(Select Count(Posts.ID) From Posts Where ThreadID = Threads.ID) as Replies
From Threads
And Class Structure:
Class Thread
Property ID as Integer
Property ThreadName as String
Property Replies as Integer
End Class
Class Post
Property ID as Integer
Property ThreadID as Integer
Property PostText as String
End Class
Any help would be much appreciated. And bonus points to supply both a LINQ example and a one using the NHibernate syntax.
So the query would be like this:
C#
var query =
from thrs in session.Query<YourNamespace.Thread>() // in C# Thread would need
select new YourNamespace.Thread // precise NS to distinguish System.Threading
{
ID = thrs.ID,
ThreadName = thrs.ThreadName,
Replies = thrs.Posts.Count()
};
var list = query.ToList(); // the above statement was executed
VB:
Dim query = From t As Thread In session.Query(Of Thread)()
Select New Thread With {
.ID = t.ID,
.ThreadName= t.ThreadName,
.Replies = t.Posts.Count
}
Dim list as List(of Thread) = query.ToList()
The very important think here is, that the Thread must have a mapping to the collection of Posts
C#
public class Thread
{
...
// really sorry for C# ... I will learn VB syntax ...
public virtual IList<Post> Posts { get; set; }
VB
Public Class Thread
Public Overridable Property Posts As IList(Of Post)
If this collection of Posts, would be mapped in NHibernate, then the above LINQ syntax will work out of the box
Related
EF Core dose not support GroupBy, and i think for Distinct i have same problem.
so, how can i fix it?
i just get 100 first elements and then i call Distinct() on result List
have a better solution?
is it possible to add groupby as a extension method to EFCore?
.Net Core is not a good idea for a reporting System :/
it is trap :(
query = query.Where(e => e.Goodscode.Contains(filter) || e.GoodsName.Contains(filter));
return query.Select(e => new GoodsDTO
{
Name = e.GoodsName,
Code = e.Goodscode,
T3Code = e.T3Code,
StockId = e.StockId
}).Take(100).ToList().Distinct(new GoodsDTOComparer()).Take(20);
//why we do like up: because EF7 dosent support Distinct and GroupBy yet (12-03-2017)
//microsoft, please don't be OpenSource, because you dont care for your opensource products
You can use Dapper library for queries that not supported in ef core
example
using (IDbConnection dbConnection = new SqlConnection(niniSiteConnectionString))
{
var sql = #"SELECT Name, Count(*) AS Total FROM Users
GROUP BY u.Name
HAVING COUNT(*) > 1";
var result = dbConnection.Query<UserDto>(sql).ToList();
}
public class UserDto
{
public string Name{get; set;}
public int Total{get; set;}
}
I typically do mobile app development, which doesn't always have .Select. However, I've seen this used a bit, but I don't really know what it does or how it's doing whatever it does. It is anything like
from a in list select a // a.Property // new Thing { a.Property}
I'm asking because when I've seen code using .Select(), I was a bit confused by what it was doing.
.Select() is from method syntax for LINQ, select in your code from a in list select a is for query syntax. Both are same, query syntax compiles into method syntax.
You may see: Query Syntax and Method Syntax in LINQ (C#)
Projection:
Projection Operations - MSDN
Projection refers to the operation of transforming an object into a
new form that often consists only of those properties that will be
subsequently used. By using projection, you can construct a new type
that is built from each object. You can project a property and perform
a mathematical function on it. You can also project the original
object without changing it.
You may also see:
LINQ Projection
The process of transforming the results of a query is called
projection. You can project the results of a query after any filters
have been applied to change the type of the collection that is
returned.
Example from MSDN
List<string> words = new List<string>() { "an", "apple", "a", "day" };
var query = from word in words
select word.Substring(0, 1);
In the above example only first character from each string instance is selected / projected.
You can also select some fields from your collection and create an anonymous type or an instance of existing class, that process is called projection.
from a in list select new { ID = a.Id}
In the above code field Id is projected into an anonymous type ignoring other fields. Consider that your list has an object of type MyClass defined like:
class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
Now you can project the Id and Name to an anonymous type like:
Query Syntax:
var result = from a in list
select new
{
ID = a.Id,
Name = a.Name,
};
Method Syntax
var result = list.Select(r => new { ID = r.Id, Name = r.Name });
You can also project result to a new class. Consider you have a class like:
class TemporaryHolderClass
{
public int Id { get; set; }
public string Name { get; set; }
}
Then you can do:
Query Syntax:
var result = from a in list
select new TemporaryHolderClass
{
Id = a.Id,
Name = a.Name,
};
Method Syntax:
var result = list.Select(r => new TemporaryHolderClass
{
Id = r.Id,
Name = r.Name
});
You can also project to the same class, provided you are not trying to project to classes generated/created for LINQ to SQL or Entity Framework.
My summary is it takes results (or a subset of results) and allows you to quickly restructure it for use in the local context.
The select clause produces the results of the query and specifies the
"shape" or type of each returned element. For example, you can specify
whether your results will consist of complete Customer objects, just
one member, a subset of members, or some completely different result
type based on a computation or new object creation.
Source: http://msdn.microsoft.com/en-us/library/bb397927.aspx
There are a lot of possible uses for this but one is taking a complex object which of many other contains a property that is a string -- say Name -- and allows you to return an enumeration with just the entries of Name. I believe you can also do the opposite -- use that property ( for example) and create / return new type of object while passing in a property or properties.
It means "mapping". Map each element of a sequence to a transformed sequence. I hadn't comprehended its meaning before I looked at the image.
Where does the meaning of the word come from?
Simply, math! https://mathworld.wolfram.com/Projection.html
I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.
I've wired up the MvcMiniProfiler to my app, and it's reporting Duplicate Queries.
I've set a BreakPoint in my Repository
Public Function Read() As System.Linq.IQueryable(Of [Event]) Implements IEventRepository.Read
Dim events = (From e In dc.Events
Select e)
Return events.AsQueryable ''# BREAKPOINT HERE
End Function
And I've hit the page in question.
My code hits the Read() function twice through my service layer (this is by design since I can't figure out how to reduce the calls)
Dim eventcount = EventService.GetHotEventCount() ''# First Hit
Dim eventlist = EventService.GetHotEvents((page - 1) * 5) ''# Second Hit
Dim model As EventsIndexViewModel = New EventsIndexViewModel(eventlist, page, eventcount)
Return View("Index", model)
The EventService does a simple query against the IQueryable Read
Public Function GetHotEvents(ByVal skip As Integer) As List(Of Domain.Event) Implements IEventService.GetHotEvents
Return _EventRepository.Read() _
.Where(Function(e) e.EventDate >= Date.Today AndAlso
e.Region.Name = RegionName) _
.OrderByDescending(Function(e) (((e.TotalVotes) * 2) + e.Comments.Count)) _
.ThenBy(Function(e) e.EventDate) _
.Skip(skip) _
.Take(5) _
.ToList()
End Function
Unfortunately I can't figure out why MiniProfiler is saying there are 8 Duplicate queries (13 in total).
Revised
So it appears as though Sam has stated that I'm not pre-loading my relationships within my queries.
How do I appropriately pre-load relationships in Linq to SQL? Can anyone lend any advice?
Edit
Here's the ViewModel that's being created.
Public Class EventsIndexViewModel
Public Property Events As List(Of Domain.ViewModels.EventPreviewViewModel)
Public Property PageNumber As Integer
Public Property TotalEvents As Integer
Public Property MapEventsList As List(Of Domain.Pocos.MapPin)
Public Property JsonMapEventsList As String
Sub New()
End Sub
Sub New(ByVal eventlist As List(Of Domain.Event), ByVal page As Integer, ByVal eventcount As Integer)
_PageNumber = page
__TotalEvents = eventcount
Dim mel As New List(Of MapPin)
_Events = New List(Of Domain.ViewModels.EventPreviewViewModel)
For Each e In eventlist
_Events.Add(New Domain.ViewModels.EventPreviewViewModel(e))
mel.Add(New MapPin(e.Location.Latitude, e.Location.Longitude, e.Title, e.Location.Name, e.Location.Address))
Next
_MapEventsList = mel
_JsonMapEventsList = (New JavaScriptSerializer()).Serialize(mel)
End Sub
End Class
Edit - added screenshot
You basically have two options to avoid SELECT n+1 with LINQ to SQL:
1) Use DataLoadOptions - http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.loadwith.aspx
DataLoadOptions enables you to specify per entity exactly that related tables should be eager-loaded. In your case, for the entity Event, you could specify LoadWith for both Comments and Locations. Whenever you load Events, Comments and Locations will then be preloaded.
The DataLoadOptions is a property you can set on the DataContext itself.
2) Use projection to fetch all the data you need in one specific query, instead of relying on lazy loading the related entities.
You have imposed a repository on top of your DataContext, so this might not be the approach you want to take, but:
Instead of selecting a list of Events and then using this entity's properties Comments and Locations, you could have your query return exactly what you need in a specific ViewModel class. LINQ to SQL would then fetch everything in a single SQL query.
I consider this the best approach if you don't absolutely NEED to abstract away the DataContext behind a repository interface. Even if you do, you could consider having the repository return View specific results, i.e.
dc.Events
.Where(something)
.Skip(something)
.Select(event => new EventViewModel
{
Event = event
Locations = event.Locations,
Comments = event.Comments
}
);
with EventViewModel being
public class EventViewModel
{
Event Event;
List<Location> Locations;
List<Comment> Comments;
}
you're going to want to .Include("Locations") and .Include("Comments") in the respective queries. I believe it goes before the .Where(), but I'm not positive about that.
The construtor 'Void .ctor(System.Guid, Int32)' is not supported.
this error occured with the following statements:
var Test = from r in db.UserRoles
join p in db.UserPermissions
on new { r.userId, r.roleId} equals new { p.userId, p.roleId }
select r;
userId is a guid
roleId is an integer
Right - the constructor for UserRoles looks like it needs a Guid and int - something you're not supplying explicitly. SubSonic has no way of figuring this out for you - one of the many reasons I keep telling people to abstract the membership stuff behind an interface and don't try to use SubSonic to get to it - you're circumventing most of their magic.