Linq query with one to many relation - linq

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

Trouble mapping to DTOs with LINQ

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; }
}

Linq select one of list where one of its child objects properties matches a value

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

Cannot implicitly convert type error when building query to populate viewmodel

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);
}

Return products which belong to all tags in a list using LINQ

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.

many to many mapping in entity framework code first

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.

Resources