I have two entites in my asp.net MVC3 application and I am using EF 4.1. Entities are:
public class Category {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Movie> Movies { get; set; }
}
public class Movie {
public int Id { get; set; }
public string Title { get; set; }
public string Director { get; set; }
public int Price {get; set;}
public DateTime ReleaseDate { get; set; }
public string Description { get; set; }
public Category Category { get; set; }
}
I want to calculate sum of prices of all movies where category name = "Comedy" using Linq query. Can you please suggest me Linq query using extension method ?
Thanks.
Assuming that you have an IEnumerable<Movie> movies, you can do
movies
.Where(x => x.Category.Name == "Comedy")
.Select(x => x.Price)
.Sum();
As #Linkgoron says in the comment, you can also put the predicate in the Sum method, such as:
movies
.Where(x => x.Category.Name == "Comedy")
.Sum(x => x.Price);
Related
i've been working with DTOs lately and am unable to determine the issue that this code is having.
I'm mapping Genre names and Movie names to a GenreMovieDto. Visual Studio doesn't show any errors (red lines etc) but when the code is run I get the following:
$exception {"The specified type member 'Movies' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."} System.NotSupportedException
My code is the following:
public class Genre
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Movie> Movies { get; set; }
}
public class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public string AgeRating { get; set; }
public int NumberInStock { get; set; }
public Genre Genre { get; set; }
}
public class GenreMovieDto
{
public string GenreName { get; set; }
public IEnumerable<Movie> Movies { get; set; }
}
And my API call:
public IEnumerable<GenreMovieDto> GetGenresWithMovies()
{
var genresWithMovies = _context.Genres
.Include(m => m.Movies)
.Select(x => new GenreMovieDto
{
GenreName = x.Name,
Movies = x.Movies <<<<< CRASHES HERE
})
.ToList();
return genresWithMovies;
}
Any thoughts ? Any and all suggestions / criticism is welcome :P I'm here to learn.
Thanks in advance
You can do like this (EF doesn't support IEnumerable<...> type member):
public class Genre
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Movie> Movies { get; set; }
}
I have these classes below
public class Match
{
public int Id { get; set; }
public MatchType Type { get; set; }
public MatchStatus Status { get; set; }
public LeagueGroup LeagueGroup { get; set; }
public Tournament Tournament { get; set; }
public IList<Score> Scores { get; set; }
public IList<Team> Teams { get; set; }
}
public class Score
{
public int Id { get; set; }
public Team Team { get; set; }
public Nullable<int> Points { get; set; }
public Match Match { get; set; }
}
public class Team
{
public int Id { get; set; }
public IList<Competitor> Competitors { get; set; }
public IList<Match> Matches { get; set; }
public Team()
{
Competitors = new List<Competitor>();
Matches = new List<Match>();
}
}
public class Competitor
{
public int Id { get; set; }
public string CompetitorName { get; set; }
public int UserId { get; set; }
public DateTime SignUpDate { get; set; }
}
So there is a Match, Which has a list of Scores (usually two), these score have a team attached to them and then each team has a list of competitors (usually 1- 2).
What I am looking to do is find the score (Points) for a Match for a certain competitor by UserId.
All of the matches will have had the user play in them so there is no need to worry about not finding a score for them.
I have tried lot of different combinations and am currently sat on :
match.Scores.FirstOrDefault(x => x.Team.Competitors.FirstOrDefault(u => u.UserId == User.UserId)).Points.Value;
I think I am going the wrong way around it though, going outwards in when i should be doing it the other way.
Any Help would be greatly appreciated
Thank you.
Found the answer.
match.Scores.FirstOrDefault(x => x.Team.Competitors.Any(u => u.UserId == User.UserId)).Points.Value;
So this basically says.
Get me the points property for the first score.
that has a team that contains a competitor that userId is equal to the User objects Id
I wanted to make a simple query to retrieve component data + language data for a specific article and then put that into a viewmodel.
The ProductComponent table is a child-table of Product and the relevant fields in it are the ComponentID and ProductId (foreign key, parentId), so I wanted to link ProductComponents to Product and ProductTranslations where I have all the language specific data, so I tried to make it all in one query to retrieve a list of components for a certain product.
Here is the query:
public IEnumerable<ProductComponents> ListComponents(int productid, string language)
{
var query = (from c in context.ProductComponents
from ct in context.ProductTranslations
where ct.ProductId == c.ComponentId
where ct.ProductLanguage == language
from cp in context.Product
where cp.ProductId == c.ComponentId
where c.ProductId == productid
select new EnumComponents
{
ProductId = c.ComponentId,
Name = ct.ProductName,
SKU = cp.SKU
});
return query;
}
That gives this error, and highlighting the return Query; part as well:
Cannot implicitly convert type 'System.Linq.IQueryable<Artikelhantering.Models.EnumComponents>' to 'System.Collections.Generic.IEnumerable<Artikelhantering.Models.ProductComponents>'. An explicit conversion exists (are you missing a cast?)
Here is most of the data model, based on what I found googling and looking through Stack Overflow the relationships between tables might be the culprit, so I am including most of it.
public class Product
{
[Key]
public int ProductId { get; set; }
[DisplayName("Article nr")]
public string SKU { get; set; }
[DisplayName("Product Category")]
public int ProductCategoriesId { get; set; }
[DisplayName("Alternative Category")]
public int? AdditionalCategoriesId { get; set; }
[DisplayName("Show purchase button?")]
public bool Purchase { get; set; }
[DisplayName("Show Product?")]
public bool ShowProduct { get; set; }
[DisplayName("Picture name")]
public string Picture { get; set; }
[DisplayName("Is reference product?")]
public bool Reference { get; set; }
[DisplayName("Inprice")]
public decimal inPrice { get; set; }
[DisplayName("Additional costs")]
public decimal AddCost { get; set; } /
[DisplayName("Weight in kg")]
public decimal Weight { get; set; }
[DisplayName("Volume in m^3")]
public decimal Volume { get; set; }
[DisplayName("Vat code, use 0")]
public decimal VAT { get; set; }
public virtual IList<ProductTranslations> ProductTranslations { get; set; }
public virtual IList<ProductPrices> ProductPrices { get; set; }
public virtual IList<ProductComponents> ProductComponents { get; set; }
public virtual IList<ProductAccessories> ProductAccessories { get; set; }
public virtual ProductCategories ProductCategories { get; set; }
public virtual ProductCampaigns ProductCampaigns { get; set; }
}
public class ProductTranslations
{
[Key]
public int ProductTranslationsId { get; set; }
public int ProductId { get; set; } // This one links to the Product class
[DisplayName("Language Code")]
public string ProductLanguage { get; set; }
[DisplayName("Description")]
public string Description { get; set; }
[DisplayName("Product Name")]
public string ProductName { get; set; }
[MaxLength(255)]
[DisplayName("Meta Keywords")]
public string MetaKeywords { get; set; }
[MaxLength(255)]
[DisplayName("Meta Description")]
public string MetaDescription { get; set; }
public virtual Product Product { get; set; }
}
public class ProductComponents
{
[Key]
public int ProductComponentsId { get; set; }
public int ProductId { get; set; }
public int ComponentId { get; set; }
public virtual IList<ProductTranslations> ProductTranslations { get; set; }
public virtual Product Product { get; set; }
}
And then I define the relationships between the models like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<ProductCategories>()
.HasMany(x => x.ProductCategoriesTranslations) // Categories has many Translations
.WithRequired(y => y.ProductCategories) // Translations require a category
.HasForeignKey(p => p.ProductCategoriesId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductPrices) // Product has many ProductPricings
.WithRequired(y => y.Product) // ProductPricing has required Product
.HasForeignKey(p => p.ProductId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductTranslations) // Product has many ProductTranslations
.WithRequired(y => y.Product) // ProductTranslations has required Product
.HasForeignKey(p => p.ProductId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductComponents) // Product has many ProductComponents
.WithRequired(y => y.Product) // ProductComponents has required Product
.HasForeignKey(p => p.ProductId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductAccessories) // Product has many ProductAccessories
.WithRequired(y => y.Product) // ProductAccessories has required Product
.HasForeignKey(p => p.ProductId);
}
I am guessing I need to define a proper relationship for ProductComponents to ProductTranslations and Product, but I am not quite sure how, I've tried various ways to create a relationship between ProductComponents -> ProductTranslations but without any success. Ofcourse the issue might well be somehing else.
Looks like I figured it out on my own. Pretty simple and obvious error now that it finally clicked. The key was to rename public IEnumerable<ListComponents> to public IEnumerable<EnumComponents>
It seems logical in retrospect, I should be using the view model here and not the model, because that's what I am trying to populate after all, I can then call the model from inside the view model just fine.
public IEnumerable<EnumComponents> ListComponents(int productid, string language)
{
//return context.ProductComponents.Where(m => m.ProductId == productid);
var query = (from c in context.ProductComponents
where c.ProductId == productid
select new EnumComponents
{
Name = c.ProductTranslations
.Where(i => i.ProductLanguage == language)
.Where(i => i.ProductId == c.ComponentId)
.Select(i => i.ProductName)
.Single(),
SKU = c.Product.SKU,
ProductId = c.ComponentId
});
return (query);
}
I have a service method which accepts a delimited list of tags and is supposed to return a list of products which are assigned to all tags in that list.
This is what I have, and it returns no products. I've double-checked the data, there IS a product that belongs to two tags.
public List<Product> GetTagProducts(string tags)
{
//list of parameters
var tagParams = tags.Split('+').ToList();
//return all products which belong to ALL tags specified in tagParams list
return (from pt in _repository.ProductTags
where tagParams.All(p => p == pt.Tag.Name)
select pt.Product).Distinct().Take(75).ToList();
}
public class Tag
{
[Key]
public int TagId { get; set; }
public string Name { get; set; }
public virtual List<Product> Products { get; set; }
public virtual List<ProductTag> ProductTags { get; set; }
}
public class Product
{
public int ProductId { get; set; }
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
[Required]
[Display(Name = "Short Description")]
public string ShortDescription { get; set; }
[Required]
[Display(Name = "Long Description")]
public string LongDescription { get; set; }
[Required]
[Display(Name = "Price")]
public decimal Price { get; set; }
public virtual List<Tag> Tags { get; set; }
}
public class ProductTag
{
[Key]
public int ProductTagId { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
[ForeignKey("Tag")]
public int TagId { get; set; }
public virtual Product Product { get; set; }
public virtual Tag Tag { get; set; }
}
//Repository
private DatabaseContext _context = new DatabaseContext();
public IQueryable<ProductTag> ProductTags
{
get { return _context.ProductTags; }
}
EDIT: to clarify the result I'm looking for. Lets say tagParams holds two tag strings (meaning I am searching for products tagged with BOTH of these):
Automotive
General
And lets say we have the following products:
product tags
------- ----
Wipers Automotive, General
Air Freshener General
Gloves General
Tires Automotive
Mirror Automotive, General
The query should return "Wipers" and "Mirror".
Method chain style:
List<Product> allProducts = GetAllProductsFromSomewhere();
allProducts.Where(p => tagParams.All(tag =>
p.Tags.Select(x => x.Name).Contains(tag))).Distinct().Take(75).ToList();
All means that all of the tags should equal to one tag. And you said it contains two tags, So it's impossible.
In MSDN words:
Determines whether all elements of a sequence satisfy a condition.
I'm using Entity Framework 4 CTP5 Code First and I have a model along the lines of:
public class User {
public int UserId { get; set; }
public string Email { get; set; }
public ICollection<Customer> TaggedCustomers { get; set; }
}
public class Customer {
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<User> TaggedBy { get; set; }
}
There is a many to many relationship where a User can 'tag' a Customer and a Customer can be 'tagged' by many users. I have a working DbContext and I can query customers using
var customers = DbContext.Customers.Include(c => c.TaggedBy);
But each customer will have all users that have tagged the customer. How do I restrict the TaggedBy collection to just result with a specifed UserId?
I've tried along the lines of DbContext.Customers.Include(c => c.TaggedBy.Select(x => x.Id == userId)); but that throws an error.
EF Feature CTP5: Fluent API Samples - ADO.NET team blog - Site Home - MSDN Blogs
modelBuilder.Entity<Product>()
.HasMany(p => p.Tags)
.WithMany(t => t.Products)
.Map(m =>
{
m.MapLeftKey(p => p.ProductId, "CustomFkToProductId");
m.MapRightKey(t => t.TagId, "CustomFkToTagId");
});
Code First Mapping Changes in CTP5 - ADO.NET team blog - Site Home - MSDN Blogs
modelBuilder.Entity<Product>()
.HasMany(p => p.SoldAt)
.WithMany(s => s.Products)
.Map(mc => {
mc.ToTable("ProductsAtStores");
mc.MapLeftKey(p => p.Id, "ProductId");
mc.MapRightKey(s => s.Id, "StoreId");
});
Mark your collections as virtual and then you can easily do:
public class User
{
public int UserId { get; set; }
public string Email { get; set; }
public virtual ICollection<Customer> TaggedCustomers { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<User> TaggedBy { get; set; }
}
using(var context = new MyDbContext())
{
var user = context.Users.Single(o => o.UserId == 0);
var customers = user.TaggedCustomers;
}
Results in much cleaner code in my opinion.