how to pass selectmany combine with groupby to MVC view - asp.net-mvc-3

i have the following lambda expression in MVC application.
var toprating= _db.Movie.SelectMany(m => m.Rating.Select(r=> new
{
movieID=r.MovieID,
MovieTitle= m.Title
})).GroupBy(m=>m.movieID).ToList();
ViewBag.TopMovie = toprating;
}
i want to pass this to my view.
i try writing the following in my view
IEnumerable<Movie> TopMovies = ViewBag.TopMovie;
but got this error
Cannot implicitly convert type 'object' to 'System.Collections.Generic.IEnumerable<Movie>'. An explicit conversion exists (are you missing a cast?)
any help will be appriciated.

i would recommend that you make a class that depicts your domain entity (Movie) like
public class MyMovie{
public int movieID { get; set; }
public string MovieTitle { get; set; }
}
and modify your linq query as
var toprating= _db.Movie.SelectMany(m => m.Rating.Select(r=> new MyMovie
{
movieID=r.MovieID,
MovieTitle= m.Title
})).GroupBy(m=>m.movieID).ToList();
ViewBag.TopMovie = toprating;
IEnumerable<MyMovie> TopMovies = ViewBag.TopMovie;
i have assumed that the Movie is your Domain entity, use view models to pass on to the views.
you may find this helpful

Related

MVC3 EF 4.1 Include Condition Linq

Apologies if this has been asked before as I am learning EF 4.1 and LINQ, I needed an expert opinion.
I have a viewmodel called HomeIndexData. My code looks something like the following:
string _categoryIDs = "100,101,102,103,104";
List < int > CategoryIDs = _categoryIDs.Split(',').Select(t => int.Parse(t));
Then I got the following view model:
public class HomeIndexData
{
public IEnumerable<Genre> Genres { get; set; }
public IEnumerable<Category> Categories { get; set; }
public IEnumerable<SubCategory> SubCategories { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
At the moment, in my Controller I have the following code -
HomeIndexData viewModel = new HomeIndexData
{
Genres = db.Genres
.Include(i => i.Categories.Where(i => CategoryIDs.Contains(i.CategoryId)))
.Include("Categories.SubCategories")
.OrderBy(g => g.DisplaySequence),
Countries = db.countries
};
But I am getting error :
Cannot convert lambda expression to type 'string' because it is not a delegate type
for the following line :
.Include(i => i.Categories.Where(i => CategoryIDs.Contains(i.CategoryId)))
Could you please put me to the right direction how can I write the lamda expression.
Any help on this will be much appreciated.
Thanks in advance.
EF doesn't support complex expressions in the Include extension at this point, only simple property getters. If you feel this is an important enhancement scenario for EF, consider voting for it at http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1015345-allow-filtering-for-include-extension-method.
For now, your best option is to project your filter in the Select clause, but that gets tricky with a generic repository.

Linq Expressions on Child entities

I am building dynamic linq expressions which is working fine for a single entity.
For example:
I have a class called Employee and empeduinfo
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
public class EmpEduInfo
{
public int Id { get; set; }
public string Name { get; set; }
public int EmpId { get; set; }
}
I need to get all the the employees and empeduinfo class starts with "x"
I prepared expression for startswith("x")
var temp= entities.employees.Include("EmpEduInfo").Where(mydynamicexpression);
In this case it is filtering only parent table not on child.
I need to prepare generic expression so than i need to filter both parent and child objects dynamically.
Without using expression I know a solution:
var temp= (from ee in entities.Employee.Include("EmpEduInfo").Where(x => x.name.StartsWith("t"))
where ee.EmpEduInfo.Where(x => x.name.StartsWith("t")).Count()>0
select ee).ToList();
using expressions I am building generic expression to provide dynamic advance search rather than writing in each and every entity.
Here is my expression details
// Get the method information for the String.StartsWith() method
MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
// Build the parameter for the expression
ParameterExpression empparam= Expression.Parameter(typeof(employee), "ename");;
// Build the member that was specified for the expression
MemberExpression field = Expression.PropertyOrField(empparam, "name");
// Call the String.StartsWith() method on the member
MethodCallExpression startsWith = Expression.Call(field, mi, Expression.Constant("t"));
var namelamda = Expression.Lambda<Func<employee, bool>>(startsWith, new ParameterExpression[] { empparam });
var temp = entities.employees.Include("empedudetails").Where(namelamda).ToList();
You can look at the Expression the compiler generates using IQueryable:
IQueryable<Employee> query =
from ee in entities.Employee ...
var expression = query.Expression;
Look at expression in a debugger to see what you need to generate - LINQPad is good for this.
You might want to simplify your query a bit first:
IQueryable<Employee> query =
from ee in entities.Employee.Include("EmpEduInfo")
where
ee.name.StartsWith("t") &&
ee.EmpEduInfo.Any(x => x.name.StartsWith("t"))
select ee;
I was trying in EF 4.0 either we have write DB extentions for the same.
Option is provided in EF 4.1
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
Thanks.

Returning a specified type from a method with EF

How can I return return a collection in a method from a LINQ query that has a one to many relationship?
For instance, I have the following code where I can have many Projects to a TimeTracking object. Will the type that I have defined for the return type (IEnumerable) work? It is set up in my EF model as this specific kind of relationship.
public IEnumerable<TimeTracking> GetTimeTrackings()
{
YeagerTechEntities DbContext = new YeagerTechEntities();
DbContext.Configuration.ProxyCreationEnabled = false;
DbContext.Configuration.LazyLoadingEnabled = false;
var timeTrackings = (from timeTrackingProjects in DbContext.TimeTrackings.Include("TimeTrackings.Projects")
select timeTrackingProjects).Where(p => p.TimeTrackingID > 0);
CloseConnection(DbContext);
return timeTrackings;
}
If so, when I display it in my MVC 3 View, and my View contains an IEnumerable<YeagerTech.YeagerTechWcfService.TimeTracking> model, will the model variable have records in it for the TimeTracking and Project objects? I don't think it will. My TimeTracking object is set up as follows unless I need to inherit the Project class with it (which would then have the Project properties):
public partial class TimeTracking
{
[DataMember]
public int TimeTrackingID { get; set; }
[DataMember]
public short ProjectID { get; set; }
[DataMember]
public byte[] Attachment { get; set; }
[Required]
[DataType(DataType.Date)]
[DataMember]
public System.DateTime StartDate { get; set; }
[Required]
[DataType(DataType.Date)]
[DataMember]
public System.DateTime EndDate { get; set; }
[DataMember]
public string Notes { get; set; }
[DataMember]
public System.DateTime CreatedDate { get; set; }
[DataMember]
public Nullable<System.DateTime> UpdatedDate { get; set; }
[DataMember]
public virtual Project Project { get; set; }
}
I also want my View to display the Project text that is associated with the TimeTracking and not the Project value. How can I do this?Can someone please help?
I got the following msg from invoking a method on my WCF client.
'cannot be serialized if reference tracking is disabled'
After getting the msg, I then modified my DataContracts to include references ([DataContract(IsReference = true)]).
namespace YeagerTechModel
{
[Serializable]
[DataContract(IsReference = true)]
public partial class Customer
{
public Customer()
{
this.Projects = new HashSet<Project>();
}
[DataMember]
public short CustomerID { get; set; }
[Required]
[StringLength(50)]
[DataType(DataType.EmailAddress)]
[DataMember]
public string Email { get; set; }
I am executing the following server side code to successfully get data from my database in a parent/child relationship. The Include method explicity invokes getting the related Project data for the specific Customer. I had to do it this way because you must turn LazyLoading off if you want to get your parent/child data across the wire.
If I look at the WCF messagelog, I can see the actual data coming across in a Customer object and it has the Project object inside of the Customer object.
However, after the call is made and I actually inspect the contents of the "customer" variable, I don't see any refernces to any Project data.
public IEnumerable<Customer> GetCustomers()
{
YeagerTechEntities DbContext = new YeagerTechEntities();
DbContext.Configuration.ProxyCreationEnabled = false;
DbContext.Configuration.LazyLoadingEnabled = false;
IQueryable<Customer> customer = DbContext.Customers.Include("Projects").Where(p => p.CustomerID > 0);
CloseConnection(DbContext);
return customer;
}
The thing I want to do now, is reference the Project data coming back from the call. However, I don't get any Customer object intellisense after typing "customer.". It's all pertains to an IQueryable object.
I'm passing it back into my MVC Controller as the following type:
IEnumerable<YeagerTechWcfService.Customer> customerList = db.GetCustomers();
and into my View as the following model:
IEnumerable<YeagerTech.YeagerTechWcfService.Customer>
Now, the big question is "How can I reference the Project data coming back in my View?
The below is my code for the View, but there is no intellisense for "item.Project". Note that "Email" is a property inside my Customer object.
foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
Looks like your Linq query should be closer to this (NOTE: did not test the query, might need tweaking):
var query = (from tt in DbContext.TimeTrackings.Include("Projects")
where tt.TimeTrackingID > 0
select tt).ToList();
Linq query as you have written is deferred execution, you are closing your connection before retrieving the data, so that would probably cause a runtime error.
.Include() statement should specify the property on the entity (TimeTracking in your case) that need to be loaded, so in this case that would be Project property
Once you have retrieved your enumerable collection of TimeTracking entities you can access the properties of the Project entity associated with a particular TimeTracking entity like so:
foreach(var tracking in GetTimeTrackings())
{
foreach(var project in tracking.Projects)
{
// Assuming your Project entity has a Name property
Response.Write(project.Name);
}
}
I'm not sure what you mean by
I also want my View to display the Project text that is associated
with the TimeTracking and not the Project value.
can you clarify what property from which entity you want to see? What is the Project Entity definition?
In response to your comment about closing connection after retrieving the data:
The statement IQueryable<Customer> customer = DbContext.Customers.Include("Projects").Where(p => p.CustomerID > 0); does not actually execute a query against the database until you start to iterate it (most likely in your view with a foreach statement). If you add a .ToList() at the end of that statement, it will execute it and return a List<Customer> (which is also IEnumerable) which contains all the records that are result of your query.
When you try to type customer. to get intellisense for the Customer entity, you're not seeing it because customer is a list of Customer entities (or rather an IQueryiable of them) so you would need to do something like customer[0]. to access the properties of the first Customer entity in that list (or iterate over it).
I'm not 100% sure how entity references come through in ASP.NET MVC on a model entity but a really simple way you can get this done is create a model class you want to use in your view, say something like this:
public class TimeTrackingModel {
public int TimeTrackingID { get; set; }
public string ProjectName { get; set; }
}
then in your query do this:
var customers = (from tt in DbContext.TimeTrackings.Include("Projects")
where tt.TimeTrackingID > 0
select new TimeTrackingModel { TimeTrackingID = tt.TimeTrackingID, ProjectName = tt.Project.ProjectName }).ToList();
then in your view specify IEnumberable<TimeTrackingModel> as the model and then access the properties like this:
foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProjectName)
</td>
Actually, after further review, I can now see the Project collection in my Customer collection all the way back up to my client after adding a QucikWatch on the object.
The correct answer is the last part of my post where the LazyLoadingEnabled = false appears.

How can i load collection with include

How can I load collection with include, I have tried these:
Type.Where(t => t.Entity.Where(e => e.Parent == false).Count() > 0).Include(e => e.Entity)
But the filter is not supported here (Entities recovered not fulfill a condition).
With LinQ To Entities :
var v = from type in ObjectContext.Type
from entity in Type.Entities
where entity.Parent == false
select type;
But type do not contains it's associated Entities.
I can't write select new Type { type.Code, type.Entities } and I can't use anonymous type as it's not adapted for my repository layer.
Have you any ideas on how I can get a list of Type objects with its property Entities satisfying a condition?
Update
Thank you for your response
I think it's not a good idea to use the CTP , no ?
I am sorry, I did not explain my object model, I have :
class Type {
public string Code { get; set; }
public IList<Entity> Entities { get; set; }
...
}
class Entity {
public string Code { get; set; }
...
}
And I want to use your second filtering proposal : Filter both entity sets
Do you have any suggestions without using the CTP ?
Thanks
Rad
as you stated you want to apply filters to the related entities, best to look at the new CTP5 of the EF4 http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
Look under "Applying filters when explicitly loading related entities"

LinqToSQl and the Member access not legal on type exception

The basic problem...
I have a method which executes the following code:
IList<Gig> gigs = GetGigs().WithArtist(artistId).ToList();
The GetGigs() method gets Gigs from my database via LinqToSql...
So, when GetGigs().WithArtist(artistId).ToList() is executed I get the following exception:
Member access 'ListenTo.Shared.DO.Artist Artist' of 'ListenTo.Shared.DO.Act' not legal on type 'System.Collections.Generic.List`1[ListenTo.Shared.DO.Act]
Note that the extension function "WithArtist" looks like this:
public static IQueryable<Gig> WithArtist(this IQueryable<Gig> qry, Guid artistId)
{
return from gig in qry
where gig.Acts.Any(act => (null != act.Artist) && (act.Artist.ID == artistId))
orderby gig.StartDate
select gig;
}
If I replace the GetGigs() method with a method that constructs a collection of gigs in code (rather than from the DB via LinqToSQL) I do NOT get the exception.
So I'm fairly sure the problem is with my LinqToSQl code rather than the object structure.
However, I have NO IDEA why the LinqToSQl version isnt working, so I've included all the associated code below. Any help would be VERY gratefully receivced!!
The LinqToSQL code....
public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
{
return from g in DBContext.Gigs
let acts = GetActs(g.ID)
join venue in DBContext.Venues on g.VenueID equals venue.ID
select new ListenTo.Shared.DO.Gig
{
ID = g.ID,
Name = g.Name,
Acts = new List<ListenTo.Shared.DO.Act>(acts),
Description = g.Description,
StartDate = g.Date,
EndDate = g.EndDate,
IsDeleted = g.IsDeleted,
Created = g.Created,
TicketPrice = g.TicketPrice,
Venue = new ListenTo.Shared.DO.Venue {
ID = venue.ID,
Name = venue.Name,
Address = venue.Address,
Telephone = venue.Telephone,
URL = venue.Website
}
};
}
IQueryable<ListenTo.Shared.DO.Act> GetActs()
{
return from a in DBContext.Acts
join artist in DBContext.Artists on a.ArtistID equals artist.ID into art
from artist in art.DefaultIfEmpty()
select new ListenTo.Shared.DO.Act
{
ID = a.ID,
Name = a.Name,
Artist = artist == null ? null : new Shared.DO.Artist
{
ID = artist.ID,
Name = artist.Name
},
GigId = a.GigID
};
}
IQueryable<ListenTo.Shared.DO.Act> GetActs(Guid gigId)
{
return GetActs().WithGigID(gigId);
}
I have included the code for the Act, Artist and Gig objects below:
public class Gig : BaseDO
{
#region Accessors
public Venue Venue
{
get;
set;
}
public System.Nullable<DateTime> EndDate
{
get;
set;
}
public DateTime StartDate
{
get;
set;
}
public string Name
{
get;
set;
}
public string Description
{
get;
set;
}
public string TicketPrice
{
get;
set;
}
/// <summary>
/// The Act object does not exist outside the context of the Gig, therefore,
/// the full act object is loaded here.
/// </summary>
public IList<Act> Acts
{
get;
set;
}
#endregion
}
public class Act : BaseDO
{
public Guid GigId { get; set; }
public string Name { get; set; }
public Artist Artist { get; set; }
}
public class Artist : BaseDO
{
public string Name { get; set; }
public string Profile { get; set; }
public DateTime Formed { get; set; }
public Style Style { get; set; }
public Town Town { get; set; }
public string OfficalWebsiteURL { get; set; }
public string ProfileAddress { get; set; }
public string Email { get; set; }
public ImageMetaData ProfileImage { get; set; }
}
public class BaseDO: IDO
{
#region Properties
private Guid _id;
#endregion
#region IDO Members
public Guid ID
{
get
{
return this._id;
}
set
{
this._id = value;
}
}
}
}
I think the problem is the 'let' statement in GetGigs. Using 'let' means that you define a part of the final query separately from the main set to fetch. the problem is that 'let', if it's not a scalar, results in a nested query. Nested queries are not really Linq to sql's strongest point as they're executed deferred as well. In your query, you place the results of the nested query into the projection of the main set to return which is then further appended with linq operators.
When THAT happens, the nested query is buried deeper into the query which will be executed, and this leads to a situation where the nested query isn't in the outer projection of the query to execute and thus has to be merged into the SQL query ran onto the DB. This is not doable, as it's a nested query in a projection nested inside the main sql query and SQL doesn't have a concept like 'nested query in a projection', as you can't fetch a set of elements inside a projection in SQL, only scalars.
I had the same issue and what seemed to do the trick for me was separating out an inline static method call that returned IQueryable<> so that I stored this deferred query into a variable and referenced that.
I think this is a bug in Linq to SQL but at least there is a reasonable workaround. I haven't tested this out yet but my assumption is that this problem may arise only when referencing static methods of a different class within a query expression regardless of whether the return type of that function is IQueryable<>. So maybe it's the class that holds the method that is at the root of the problem. Like I said, I haven't been able to confirm this but it may be worth investigating.
UPDATE: Just in case the solution isn't clear I wanted to point it out in context of the example from the original post.
public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
{
var acts = GetActs(g.ID); // Don't worry this call is deferred
return from g in DBContext.Gigs
join venue in DBContext.Venues on g.VenueID equals venue.ID
select new ListenTo.Shared.DO.Gig
{
ID = g.ID,
Name = g.Name,
Acts = new List<ListenTo.Shared.DO.Act>(acts),
Description = g.Description,
StartDate = g.Date,
EndDate = g.EndDate,
IsDeleted = g.IsDeleted,
Created = g.Created,
TicketPrice = g.TicketPrice,
Venue = new ListenTo.Shared.DO.Venue {
ID = venue.ID,
Name = venue.Name,
Address = venue.Address,
Telephone = venue.Telephone,
URL = venue.Website
}
};
}
Note that while this should correct the issue at hand there also seems to be another issue in that the deferred acts query is being accessed in each element of the projection which I would guess would cause separate queries to be issued to the database per row in the outer projection.
I don't see anything in your classes to indicate how LINQ to SQL is meant to work out which column is which, etc.
Were you expecting the WithArtist method to be executed in .NET, or converted into SQL? If you expect it to be converted into SQL, you'll need to decorate your Gig class with appropriate LINQ to SQL attributes (or configure your data context some other way). If you want it to be executed in code, just change the first parameter type from IQueryable<Gig> to IEnumerable<Gig>.
I found out that an issue like this (which I also had recently) can be resolved, if you convert the IQueryable (or Table) variable Gigs into a list like so
return from g in DBContext.Gigs.ToList()
...
If that still doesn't work, do the same for all the IQueryables. The reason behind seems to me that some queries are too complex to be translated into SQL. But if you "materialize" it into a list, you can do every kind of query.
Be careful, you should add "filters" (where conditions) early because too much memory consumption can become a problem.

Resources