Linq, how to Group by Field, Sum on Field, First on Field - asp.net-mvc

Hi i'm having a little trouble with the following
I have a table (CallRecords) with an navigation property to another table (ResultCodes)
I want to perform a GroupBy from (CallRecords) on ResultCodesId
Sum (The occurrences of ResultCodesId)
First on an included table and field ResultCodes.Name, I.e the name of the resultCode (via navigation property)
var exceptions = Db.CallRecords
.Include(x => x.ResultCode)
.Where(x => x.ClientId == id && x.ResultCodeId < 0 && x.StartTime >= startDate && x.StartTime <= finishDate)
.GroupBy(o => o.ResultCodeId)
.Select(g => new ExceptionViewModel
{
Code = g.Key ?? 0,
Total = g.Count(),
Name = g.First(x => x.ResultCode.Name)
});
This is the problem, the following line wont compile
Name = g.First(x => x.ResultCode.Name)
cannot convert expression type 'string' to return type bool
The answer to this would (seemingly) be fairly simple, however my Google and stack searches are given me back everything except examples i need, so i thought an answer to this might help other unwary travelers
Update
Additional Info
View model
public class ExceptionViewModel
{
public int Code { get; set; }
public int Total { get; set; }
public String Name { get; set; }
}
Data
public class ResultCode
{
[Required,Key]
public int Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class CallRecord
{
[Required]
public int Id { get; set; }
// other properties removed for brevity
[Required]
public int? ResultCodeId { get; set; }
public virtual ResultCode ResultCode { get; set; }
}
As you can see all properties involved with that problem expression above are of type string, im not sure whether i'm having a brain-fart or there is something i just dont understand

The expression g.First(x => x.ResultCode.Name) does not do what you think it does. When First has an argument that argument is supposed to be a predicate that filters the enumerable sequence.
In other words, .First(x => ...) is equivalent to .Where(x => ...).First(). If you look at it this way it's clear that x.ResultCode.Name is not valid in that context.
What you want is to get the first item in each group and then fish out some data from it. Do that like so:
g.First().ResultCode.Name

Related

ASP.Net MVC - Unable to cast the LINQ query result into ViewModel

I am trying to bind a tree view menu control in a partialview with the ViewModel.
I have created a nested viewModel assuming that is the rightway to carry all the nested data needed for the navigation tree menu at one time and so I have wrote my linq query to fetch the data in a nested fashion. As newbie I am not 100% sure that this is the right way to do it.
I am trying to bind my view model with Linq query result. My Model is nested and so is my Linq query. I am facing difficulty molding this two together. What ever way I try I am getting type casting error such as cannot convert from Type IQueriable to IList of collections.. My code:
// ViewModel
namespace My.Namespace
{
public class MyViewModel
{
public decimal CategoryID { get; set; }
public string CategoryName { get; set; }
public decimal Badge { get; set; }
public IList<SubCategories> CategorySubCategories { get; set; }
}
public class SubCategories
{
public decimal SubCategoryID { get; set; }
public string SubCategoryName { get; set; }
public decimal Badge { get; set; }
public List<Items> SubCategoryItems { get; set; }
}
public class Items
{
public decimal ID { get; set; }
public string ItemName { get; set; }
public List<SubItems> SubItems { get; set; }
}
public class SubItems
{
public decimal ID { get; set; }
public string SubItemName { get; set; }
}
}
//Databinding code:
MyViewModel result = new MyViewModel();
var query= (List<MyViewModel>)(from c in dbContext.TableName
where c.CHILD_ID == 0
select new MyViewModel
{
CategoryID = c.ELEMENT_ID,
CategoryName = c.CHILD_DESC,
CategorySubCategories = (List<SubCategories>)(from s in dbContext.TableName
where s.PARENT_ID == c.ELEMENT_ID
select new SubCategories
{
SubCategoryID = s.ELEMENT_ID,
SubCategoryName = s.CHILD_DESC,
SubCategoryItems = (List<Items>)(from i in dbContext.TableName
where i.PARENT_ID == s.ELEMENT_ID
select new Items
{
ID = i.ELEMENT_ID,
ItemName = i.CHILD_DESC
}
)
}
)
});
return query.toList();
The error I am getting is # line: var query = (List)(from c in dbContext.TableName
Error:
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery1[MyViewModel]' to type 'System.Collections.Generic.List1[MyViewModel]'.
On your view model, change your List types to IEnumerable types. Make sure not to use the .ToList() extension method on those sub selects.
The resulting types of your sub select statements are not instances of List, but do implement the IEnumerable interface. They may implement ICollection, IList, etc, but you can play around with that.

The properties expression is not valid. The expression should represent a property

I have these two entities
public class Song : IPathHavingEntity
{
public int Id { get; set; }
[Required]
public string Path { get; set; }
[Required]
public virtual Album Album { get; set; }
[Required]
public int TrackNumber { get; set; }
}
public class Album : IPathHavingEntity
{
public int Id { get; set; }
[Required]
public string Path { get; set; }
public virtual IEnumerable<Song> Songs { get; set; }
[Required]
public int AlbumNumber { get; set; }
}
Path is defined in the IPathHavingEntity interface.
In my Seed method I want to add a song to the Songs table only if it doesn't exist. For this reason I check that the album path and song path combination don't exist already before adding it thus
context.Songs.AddOrUpdate(
s => new { FilePath = s.Path, AlbumPath = s.Album.Path },
new Song { TrackNumber = 1, Path = "01 Intro.mp3", Album = one });
The problem is I get this error
The properties expression 's => new <>f__AnonymousType0``2(FilePath =
s.Path, AlbumPath = s.Album.Path)' is not valid. The expression should
represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple properties use an anonymous type: C#: 't => new { t.MyProperty1, t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1, t.MyProperty2 }'.
What's the problem?
In my case, The Only modification I did that on the Model Classes forget to put {get; set;} with the property declaration, Thus ...It's solved my problem.
Like this:
Before:
public int Supplier_ID;
public String Supplier_Code;
After:
public int Supplier_ID { get; set; }
public String Supplier_Code { get; set; }
Kindly Check your Model Classes should have the Get/Set property
I struggled with a similar issue for several hours today and was finally able to resolve it. I'm not sure if this will work for your situation but it's worth investigating.
The problem may be caused by the Album property of your Song entity being marked as virtual. I'm not an EF expert but I don't think it likes that virtual property when initializing your anonymous type. Add a non-virtual property for the album path (but keep the virtual navigation property), like this:
public class Song : IPathHavingEntity
{
public int Id { get; set; }
[Required]
public string Path { get; set; }
[Required]
public virtual Album Album { get; set; }
public string AlbumPath { get; set; }
[Required]
public int TrackNumber { get; set; }
}
And then perform the AddOrUpdate using that non-virtual property, like this:
context.Songs.AddOrUpdate(
s => new { FilePath = s.Path, AlbumPath = s.AlbumPath },
new Song { TrackNumber = 1, Path = "01 Intro.mp3", Album = one });
EF should then only allow you to add songs where the given song path and album path do not already exist. Whether your Song domain object can have a non-virtual AlbumPath property is another question but this should at least allow you to run your seed method in the way you described.
EF does not create a column for a property which does not have either getters or setters. For example, EF will not create columns for the following City and Age properties.
using System.ComponentModel.DataAnnotations;
public class Student
{
private int _age = 0;
public int StudentId { get; set; }
public string StudentName { get; set; }
public string City { get{ return StudentName;} }
public int Age { set{ _age = value;} }
}
Referance : https://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-first.aspx
In my case, changing the following values in the mapper worked.
From:
this.HasKey(t => new { FirstName = t.FirstName, LastName = t.LastName });
To:
this.HasKey(t => new { t.FirstName, t.LastName });
What has not been mentioned in any of other answers is that the source of the problem in any case shown is the same: The "custom identification expression" passed in as a parameter of the AddOrUpdate method has to be a valid property of the entity being inserted or updated. Also, it will not accept ComplextType.Property there.
For example:
context.Songs.AddOrUpdate(
s => new { k.Path, k.AlbumPath },
new Song { TrackNumber = 1, Path = "01 Intro.mp3", Album = one });
Note that the problem was resolved when the AlbumPath was used and also note that the anonymous type requires no other fields to be created. Instead, you just need to specify the property names.
It worth mentioning to be careful when using AddOrUpdate as the result can be destructive.

Best way to approach activity based feeds in asp.net and ravendb

I will have an activity feed per user that displays all activity related to events the user is subscribed to and the feed will pull in the most current 20 or so activities. The way I have it setup is all activity regardless of the event its related to is stored in one collection and the document itself has an "event" property I query against and index. The basic query is just select activities from collection where event is in the users event subscription list ordered by date. I store a hash of the list of the users event subscriptions and cache the results of the query using the hash as the key for xx seconds so if another user is subscribed to same exact events I can pull the results from cache instead, I'm not concerned with results being xx seconds stale.
Edit: added model and query example
Models:
User
{
// useless properties excluded
// fixed: hashset not list, can be quite large
HashSet<string> Subscriptions { get; set; }
string SubscriptionHash { get; set; } // precomputed hash of all strings in subscriptions
}
Activity
{
// Useless properties excluded
string ActivityType { get; set; }
}
Query:
if (cache[user.SubscriptionHash] != null)
results = (HashSet<Activity>)cache[user.SubscriptionHash];
else
results = session.Query<Activity>().Where(user.Subscriptions.Contains(e => e.ActivityType)).Take(20).ToList();
// add results to cache
My concern is if this is the BEST APPROACH to handle this or if there's better ravendb voodoo to use. The single collection could grow into the millions if there's alot of activities and I could potentially be storing thousands of keys in the cache when there's thousands of users with endless combinations of subscription lists. These feeds are on the users landing page so it gets hit alot and I don't want to just throw more hardware at the problem.
So answer im really looking for is if this is the best query to use or if there's a better way to do it in Raven when I could be querying against millions of documents using list.Contains.
This is an asp.net 4.5 mvc 4 project using ravendb.
Now here is how I would approach it. This is based on RaccoonBlog PostComments
I would store each users events in a separate document (i.e. UserEvent in the example below) with the user having an additional property linking to it along with a number of events and a timestamp of the last event associated with the user. This would keep the user document much smaller but having alot of the important information
In UserEvent it would be a simple document holding id, link to the userid this document references, a "event" collection, and a lasteventid. This way each "event" becomes a sub document for maintenance if needed.
Lastly a Index on UserEvent that allows you to query the data easily
public class User
{
public string Id { get; set; }
// other user properties
public string UserEventId { get; set; }
public int NumberOfEvents { get; set; }
public DateTimeOffset LastEvent { get; set; }
}
public class UserEvent
{
public string Id { get; set; }
public string UserId { get; set; }
public int LastEventId { get; set; }
public ICollection<Event> Events { get; protected set; }
public int GenerateEventId()
{
return ++LastEventId;
}
public class Event
{
public int Id { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public string ActivityType { get; set; }
// other event properties
}
}
public class UserEvents_CreationDate : AbstractIndexCreationTask<UserEvent, UserEvents_CreationDate.ReduceResult>
{
public UserEvents_CreationDate()
{
Map = userEvents => from userEvent in userEvents
from evt in userEvent.Events
select new
{
evt.CreatedAt,
EventId = evt.Id,
UserEventId = userEvent.Id,
userEvent.UserId,
evt.ActivityType
};
Store(x => x.CreatedAt, FieldStorage.Yes);
Store(x => x.EventId, FieldStorage.Yes);
Store(x => x.UserEventId, FieldStorage.Yes);
Store(x => x.UserId, FieldStorage.Yes);
Store(x => x.ActivityType, FieldStorage.Yes);
}
public class ReduceResult
{
public DateTimeOffset CreatedAt { get; set; }
public int EventId { get; set; }
public string UserEventId { get; set; }
public string UserId { get; set; }
public string ActivityType { get; set; }
}
}
public static class Helpers
{
public static DateTimeOffset AsMinutes(this DateTimeOffset self)
{
return new DateTimeOffset(self.Year, self.Month, self.Day, self.Hour, self.Minute, 0, 0, self.Offset);
}
public static IList<Tuple<UserEvents_CreationDate.ReduceResult, User>> QueryForRecentEvents(
this IDocumentSession documentSession,
Func
<IRavenQueryable<UserEvents_CreationDate.ReduceResult>, IQueryable<UserEvents_CreationDate.ReduceResult>
> processQuery)
{
IRavenQueryable<UserEvents_CreationDate.ReduceResult> query = documentSession
.Query<UserEvents_CreationDate.ReduceResult, UserEvents_CreationDate>()
.Include(comment => comment.UserEventId)
.Include(comment => comment.UserId)
.OrderByDescending(x => x.CreatedAt)
.Where(x => x.CreatedAt < DateTimeOffset.Now.AsMinutes())
.AsProjection<UserEvents_CreationDate.ReduceResult>();
List<UserEvents_CreationDate.ReduceResult> list = processQuery(query).ToList();
return (from identifier in list
let user = documentSession.Load<User>(identifier.UserId)
select Tuple.Create(identifier, user))
.ToList();
}
}
Then all you have to do to query is something like.
documentSession.QueryForRecentEvents(q => q.Where(x => x.UserId == user.Id && x.ActivityType == "asfd").Take(20)).Select(x => x.Item1);

Search within navigation properties

I have created my application by using code-first approach in ASP.NET MVC 4.
I have three entities. Namely, "Company", "Service" and "ServiceFeature":
public class Company
{
public int CompanyID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public virtual Service SuppliedService { get; set; }
}
public class Service
{
public int ServiceID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<ServiceFeature> ServiceFeatures { get; set; }
}
public class ServiceFeature
{
public int ServiceFeatureID { get; set; }
[Required]
public string Name { get; set; }
}
I have a search form, it consists of checkboxes for all ServiceFeatures. User will select checkboxes and get the results of Companies that are providing the Services with selected ServiceFeatures.
I get the Company list with my service as below but I'm stuck at how to include the selected ServiceFeatures in a where clause (dynamic LINQ with a for loop?)
var searchResults = _companyService.GetCompanies();
Assuming that you have a collection containing the IDs of the selected features, called requested, and assuming you want the companies supplying a service which contains ALL the selected features, you could for example do this:
var q = from c in searchResults
let sf = c.SuppliedService.ServiceFeatures
.Select(f => f.ServiceFeatureID)
.Intersect(requested)
where sf.Count() == requested.Count()
select c;
In similar cases I prefer an approach that's a bit more elaborate than a linq query with Intersect. Intersect can produce horrible queries with deep nesting, because the list with Id values to intersect over is built by SELECT and UNION commands for each Id in the list. This is not a problem when the number of Id's is low (true in your case, I assume), but with larger numbers it may throw a SQL exception.
So this is what I'd prefer:
var q = context.Companies.AsQueryable();
foreach(int i in featureIds)
{
int j = i; // prevent modified closure.
q = q.Where(c => c.SuppliedService.ServiceFeatures.Any(f => f.Id == j));
}
var result = q.ToList();
It builds a query with a number of WHERE EXISTS clauses. This can be very efficient because each EXISTS will seek (not scan) the primary index of ServiceFeature. Besides that, an INTERSECT is an implicit DISTINCT.
Well, just wanted to point this out. As said, with low numbers of records you won't notice any difference. So take whatever suits you best.

Unable to create a constant value of type 'T'. Only primitive types ('such as Int32, String, and Guid') are supported in this context

I see alot of question about this error, but some one can tell me why I get this error in my code?
I have the User and Group in my app, they have many to many relationship:
public class Group
{
[Key]
public int GId { get; set; }
public string GName { get; set; }
public virtual ICollection<UserProfile> Members { get; set; }
public Group()
{
Members = new HashSet<UserProfile>();
}
}
public class UserProfile
{
[Key]
public Guid UserId { get; set; }
[Required]
public string UserName { get; set; }
public virtual ICollection<Group> Groups { get; set; }
public UserProfile()
{
Groups = new HashSet<Group>();
}
}
I want to get all the Group that a User has Join ans pass it to a ViewBag, so:
UserProfile user = core.Profiles.Find(1);
//ok, no error in controller, but view...
ViewBag.JoinGroups = core.Groups.Where(g => g.Members.Contains(user));
But I get the error at View:
#if (ViewBag.JoinGroups != null)
{
foreach (var g in ViewBag.JoinGroups)//My issue start here
{
<p>#g.GId</p>
}
}
And it said:
Unable to create a constant value of type 'Project.Model.UserProfile'.
Only primitive types ('such as Int32, String, and Guid') are supported
in this context.
Do I missing any thing?
The message is clear: EF Linq queries do not support passing entities.
You can work around it by changing this piece:
UserProfile user = core.Profiles.Find(1);
ViewBag.JoinGroups = core.Groups.Where(g => g.Members.Contains(user));
For this:
ViewBag.JoinGroups = core.Groups.Where(g => g.Members.Select(x => x.UserId)
.Contains(1));
This is not specific to a ViewBag or anything. It's just the deferred execution pitfall: not until the foreach is the query executed. You would have seen the exception earlier by doing core.Groups.Where(g => g.Members.Contains(user)).ToList();.
By the way you pose it is clear that you already know that referencing non-scalar variables is not supported in entity framework, but allow me to mention it for the sake of completeness.

Resources