C# linq group by multiple times - c#

I have a an object Quiz that looks like :
public class Quiz
{
public int Id { get; set; }
public ICollection<MathQuiz> MathQuizzes { get; set; }
}
MathQuizze object looks like :
public class MathQuiz
{
public int Id { get; set; }
public int QuizId{ get; set; }
public Quiz Quiz{ get; set; }
public int AnswerId { get; set; }
public Answer Answer { get; set; }
public int TagId{ get; set; }
public Tag Tag { get; set; }
public bool IsCorrect { get; set; }
}
And have an object(UserQuizzes) that looks like:
public class UserQuizes
{
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int QuizId { get; set; }
public Quiz Quiz { get; set; }
}
UserQuizzes is just class that express many to many relationship between users and quizzes.
This is a sample data :
List<Quiz> quizzes = new List<Quiz>();
quizzes.Add(new Quiz{ Id = 1, MathQuizzes = new List<MathQuiz>{
new MathQuiz { AnswerId = 58, TagId = 1, IsCorrect = false },
new MathQuiz { AnswerId = 26, TagId = 2, IsCorrect = true },
new MathQuiz { AnswerId = 57, TagId = 3, IsCorrect = true },
new Quiz{ Id = 2, MathQuizzes = new List<MathQuiz>{
new MathQuiz { AnswerId = 59, TagId = 1, IsCorrect = false },
new MathQuiz { AnswerId = 87, TagId = 2, IsCorrect = true },
new MathQuiz { AnswerId = 25, TagId = 3, IsCorrect = true }, });
List<UserQuizzes> userQuizzes = new List<UserQuizzes>();
userQuizzes.Add(new Quiz{ QuizId = 1, UserId = 1},
userQuizzes.Add(new Quiz{ QuizId = 2, UserId = 1});
Please don't spend too much time criticizing, I just wanted to use something that everyone is pretty familiar with.
What i want to achieve is that group by userquizzes by MathQuiz TagId and get data something like this:
TagId : 1, IsCorrect: true(0), false(2);
TagId : 2, IsCorrect: true(2), false(0);
TagId : 3, IsCorrect: true(2), false(0);

The code you posted has typos, but here is the basic idea:
var query =
from q in quizzes
from mq in q.MathQuizzes
join uq in userQuizzes on q.Id equals uq.QuizId
group mq by mq.TagId into g
select new
{
TagId = g.Key,
Correct = g.Sum(e => e.IsCorrect ? 1 : 0),
Incorrect = g.Sum(e => e.IsCorrect ? 0 : 1)
};
Basically you need to get the effective source set by joining the data sets, and the do the regular grouping/calculating aggregates.

Related

I'm getting a "Collection was of a fixed size error" when trying to add objects to a DbSet in an in-memory database

I'm working on writing an API using .NET 6. I've encountered a "Collection was of a fixed size error" error, which I've never seen before. Looking here on SO I haven't seen anything that addresses this situation. Here's the DbContext class:
using Microsoft.EntityFrameworkCore;
namespace ECommerce.Api.Orders.Db
{
public class OrdersDbContext : DbContext
{
public OrdersDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{
}
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
}
}
Here's the Order class:
namespace ECommerce.Api.Orders.Db
{
/*
* This is the in-memory class for Orders
*/
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public DateTime OrderDate { get; set; }
public decimal Total { get; set; }
public ICollection<OrderItem>? Items { get; set; } }
}
And the OrderItem class:
namespace ECommerce.Api.Orders.Db
{
/*
* This is the in-memory class for OrderItems
*/
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
}
Now comes the OrdersProvider class. In OrdersProvider I create an orderItems array, an orderItemsTotals array and 3 arrays of type OrderItem which I use to put into new values in the instances I add to the in-memory Orders table. The error comes on the second Add() method in Step 3:
private void SeedData()
{
if (!dbContext.Orders.Any())
{
// Step 1: Start by creating a collection of 3 OrderItems
OrderItem[] orderItems = new OrderItem[]
{
new OrderItem() {Id = 1, OrderId = 1, ProductId = 1, Quantity = 5, UnitPrice = 5.5M},
new OrderItem() {Id = 2, OrderId = 1, ProductId = 2, Quantity = 7, UnitPrice = 13.75M },
new OrderItem() {Id = 3, OrderId = 2, ProductId = 3, Quantity = 10, UnitPrice = 25.99M}
};
// need to get the totals of the values created above, into its own array
decimal[] orderItemTotals = new decimal[3];
for (int i = 0; i < 3; i++)
{
orderItemTotals[i] = orderItems[i].Quantity * orderItems[i].UnitPrice;
}
// Step 2: Need three separate array types to use in the creation of Orders
OrderItem[] orderItems1 = new OrderItem[2]
{
orderItems[0],
orderItems[2]
};
OrderItem[] orderItems2 = new OrderItem[2]
{
orderItems[0],
orderItems[1]
};
OrderItem[] orderItems3 = new OrderItem[1]
{
orderItems[2]
};
// Step 3: Then create a dbContext of Orders the 3 OrderItems above
dbContext.Orders.Add(new Db.Order()
{
Id = 1,
CustomerId = 1,
OrderDate = new DateTime(2022, 1, 17),
Items = orderItems1,
Total = orderItemTotals[0] + orderItemTotals[2]
});
dbContext.Orders.Add(new Db.Order()
{
Id = 2,
CustomerId = 2,
OrderDate = new DateTime(2020, 7, 1),
Items = orderItems2,
Total = orderItemTotals[0] + orderItemTotals[1]
});
dbContext.Orders.Add(new Db.Order()
{
Id = 3,
CustomerId = 3,
OrderDate = new DateTime(2018, 10, 31),
Items = orderItems3,
Total = orderItemTotals[2]
});
dbContext.SaveChanges();
}
}
At this point I don't understand why I'm getting that error when adding the second element after Step 3 in the code immediately above. I would think that Orders is a table; there should be nothing fixed about it. Or it might be related to Items in Order class, but Items is of nullable type of ICollection<OrderItem>. That shouldn't complain about being fixed, either.
So, where am I making my mistake?

Updating multiple Array elements in mongoDB not working

C# MongoDB Driver
public class Details
{
[BsonId]
public ObjectId Id { get; set; }
public string GroupID { get; set; }
public Names[] Name{ get; set; }
}
public class Names
{
public string FullName { get; set; }
public int Age { get; set; }
public int Status {get; set;}
}
Input
var req = [{FullName = "ABC",
Age = 15,
Status = 0}
{FullName = "XYZ",
Age = 16,
Status = 0},
{FullName = "QAZ",
Age = 14,
Status = 0}]
MongoDB Query
var updateDefinationValues = new List<UpdateDefinition<Details>>();
List<FilterDefinition<Details>> listDetailsFilter = new List<FilterDefinition<Details>>();
foreach (var il in req.Names)
{
FilterDefinition<Details> detailsFilter = Builders<Details>.Filter.Where(x => x.GroupID == requestId && x.Names.Any(i => i.FullName == il.FullName));
updateDefinationValues.Add(Builders<Details>.Update.Set(x => x.Names.ElementAt(-1).Status, 1));
listDetailsFilter.Add(detailsFilter);
}
FilterDefinition<Details> filter = Builders<Details>.Filter.Or(test);
var combinedUpdate = Builders<Details>.Update.Combine(updateDefinationValues);
var isUpdated = UpdateOne(_db, filter, combinedUpdate);
The above query is working when listDetailsFilter count == 1.
Error: The positional Operation did not find the match needed for this query
And it is not working when listDetailsFilter count > 1.

Use LINQ to join 2 objects for each of its properties

I have created 2 models to store the results of an sql query. Now I would like to join them for each of the week's... (week1 = Record_id, week2 = Record_id)
to get a new Object in which I would have all the data from the 1st model, as well as map data from the "Category" Model to it.
I created a new Model for it, but I am not sure how to write a linq query
First Model:
public class CustomData
{
public string full_name { get; set; }
public string location { get; set; }
public int week1 { get; set; }
public int week2 { get; set; }
public int week3 { get; set; }
}
Second Model:
public class Category
{
public int Record_ID { get; set; }
public int Color{ get; set; }
public string Name { get; set; }
}
New Model for end result:
public class WeekView
{
public string full_name { get; set; }
public string location { get; set; }
public Category week1 { get; set; }
public Category week2 { get; set; }
public Category week3 { get; set; }
}
This should work:
List<CustomData> list = new List<CustomData>();
list.Add(new CustomData() { full_name = "test", location = "test", week1 = 0, week2 = 1, week3 = 2 });
list.Add(new CustomData() { full_name = "test2", location = "test2", week1 = 0, week2 = 12, week3 = 22 });
List<Category> categories = new List<Category>();
categories.Add(new Category { Color = 0, Name = "testName", Record_ID = 0 });
categories.Add(new Category { Color = 1, Name = "testName1", Record_ID = 1 });
categories.Add(new Category { Color = 2, Name = "testName2", Record_ID = 2 });
categories.Add(new Category { Color = 3, Name = "testName3", Record_ID = 12 });
categories.Add(new Category { Color = 4, Name = "testName4", Record_ID = 22 });
List<WeekView> results = new List<WeekView>();
results.AddRange(list.Select(x=>
new WeekView() { full_name = x.full_name,
location = x.location,
week1 = categories.FirstOrDefault(c => c.Record_ID == x.week1),
week2 = categories.FirstOrDefault(c => c.Record_ID == x.week2),
week3 = categories.FirstOrDefault(c => c.Record_ID == x.week3)
}));
Try out the following:
var result = (from cd in CustomDatas
join ca1 in Categories on cd.week1 equals ca.Record_ID into ca1r
from ca1 in ca1r.DefaultIfEmpty()
join ca2 in Categories on cd.week2 equals ca.Record_ID into ca2r
from ca2 in ca2r.DefaultIfEmpty()
join ca3 in Categories on cd.week3 equals ca.Record_ID into ca3r
from ca3 in ca3r.DefaultIfEmpty()
select new {
full_name = cd.full_name,
location = cd.location,
week1 = ca1,
week2 = ca2,
week3 = ca3
}

How to query a lookup table using a list?

Given the classes below, I want to be able to use a List of ids to return designs that have the AttributeId of 1 or 3 in the DesignAttribute table.
public class Design
{
public int DesignId { get; set; }
public string DesignName { get; set; }
public virtual List<DesignAttribute> DesignAttributes { get; set;}
}
public class Attribute
{
public int AttributeId { get; set; }
public string AttributeName { get; set; }
}
public class DesignAttribute
{
public int DesignAttributeId { get; set; }
public virtual Design Design { get; set; }
public virtual Attribute Attribute { get; set; }
}
A design can have 1 or more attributes, for example
Design Table
DesignId DesignName
1 Design A
2 Design B
3 Design C
Attribute Table
AttributeId AttributeName
1 Light
2 Dark
3 Demo
DesignAttribute Table
DesignAttributeId Design_DesignId Attribute_AttributeId
1 1 1 Design A is Light
2 1 3 Design A is also a Demo
3 2 2 Design B is Dark
4 3 1 Design C is Light
I've got the following code
//attributes list = "[1,3] I want any designs that have Light OR Demo attributes"
public List<Design> FilterDesigns(List<string> attributes)
{
//sudo code as i'm not sure how to structure this.
var designs = db.Designs.Where(i => i.DesignAttributes
WHERE DesignAttributes.AttributeId is in the list of attributes passed into the method)
}
So i'd hopefully end up with a list of 2 items containing the designs Design A and Design C, as they both have an ID against the Attribute Id 1 and 3 in the DesignAttribute lookup table.
Try this :
var designs = db.Designs.Where(design =>
design.DesignAttributes.Any(designAttribute =>
attributes.Contains(designAttribute.Attribute.AttributeId)))
.ToList();
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication29
{
class Program
{
static void Main(string[] args)
{
List<Design> designTable = new List<Design>() {
new Design() { DesignId = 1, DesignName = "A"},
new Design() { DesignId = 2, DesignName = "B"},
new Design() { DesignId = 3, DesignName = "C"}
};
List<Attribute> attributeTable = new List<Attribute>() {
new Attribute() { AttributeId = 1, AttributeName = "Light"},
new Attribute() { AttributeId = 2, AttributeName = "Dark"},
new Attribute() { AttributeId = 3, AttributeName = "Demo"}
};
List<DesignAttribute> designAttributeTable = new List<DesignAttribute>() {
new DesignAttribute() { DesignAttributeId = 1, DesignId = 1, AttributeId = 1},
new DesignAttribute() { DesignAttributeId = 2, DesignId = 1, AttributeId = 3},
new DesignAttribute() { DesignAttributeId = 3, DesignId = 2, AttributeId = 2},
new DesignAttribute() { DesignAttributeId = 4, DesignId = 3, AttributeId = 1}
};
var results = (from dattbl in designAttributeTable
join dttbl in designTable on dattbl.DesignId equals dttbl.DesignId
join attbl in attributeTable on dattbl.AttributeId equals attbl.AttributeId
select new { designName = dttbl.DesignName, attributeName = attbl.AttributeName }).ToList();
}
}
public class Design
{
public int DesignId { get; set; }
public string DesignName { get; set; }
public virtual List<DesignAttribute> DesignAttributes { get; set; }
}
public class Attribute
{
public int AttributeId { get; set; }
public string AttributeName { get; set; }
}
public class DesignAttribute
{
public int DesignAttributeId { get; set; }
public int DesignId { get; set; }
public int AttributeId { get; set; }
}
}
You can try to use this query:
var ids = new List<int> { 1, 3 };
var designs = db.DesignAttributes
.Where(m => ids.Contains(m.DesignAttributeId))
.Select(p => p.Design)
.ToList();
It will query DesignAttributes where DesignAttributeId are present in list ids. Than it will select Designs.

Error null reference in left join

When i comment dishes.Add(new Dishes { DishID = 8, DishName = "Name", DishTypeID = 2, IngredientID = 2 }); i get in ll one item Amount="1 cup" DishID=1 Ingridient="egg" Name ="Soup". When uncomment that line raising error, null reference exception in b.IngredientTypeID. The main question how to get in ll two item's:
1) Amount="1 cup" DishID=1 Ingridient="egg" Name ="Soup"
2) Amount=null DishID=2 Ingridient=null Name =null
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Dishes> dishes = new List<Dishes>();
List<Ingredients> ingredients = new List<Ingredients>();
List<Amount> amount = new List<Amount>();
List<Ingredient> ingredient = new List<Ingredient>();
dishes.Add(new Dishes { DishID = 1, DishName = "Soup", DishTypeID = 1, IngredientID = 1 });
//dishes.Add(new Dishes { DishID = 8, DishName = "Name", DishTypeID = 2, IngredientID = 2 });
ingredients.Add(new Ingredients { AmountID = 2, IngredientID = 1, IngredientTypeID = 1, IngredientUniqID = 1 });
amount.Add(new Amount { AmountID = 2, AmountName = "1 cup" });
ingredient.Add(new Ingredient { IngredientID = 1, IngredientName = "egg" });
var test = from dish in dishes
join ing in ingredients on dish.IngredientID equals ing.IngredientID into result
from b in result.DefaultIfEmpty()
join i in ingredient on b.IngredientTypeID equals i.IngredientID into r
from c in r.DefaultIfEmpty()
join am in amount on b.AmountID equals am.AmountID into s
from t in s.DefaultIfEmpty()
select new DisplayRecipe { Name = dish.DishName, Amount = t.AmountName, Ingredient = c.IngredientName, DishID = dish.DishID };
List<DisplayRecipe> ll = test.ToList();
}
}
public partial class Dishes
{
public int DishID { get; set; }
public string DishName { get; set; }
public Nullable<int> DishTypeID { get; set; }
public Nullable<int> IngredientID { get; set; }
}
public partial class Ingredients
{
public int IngredientID { get; set; }
public Nullable<int> AmountID { get; set; }
public Nullable<int> IngredientTypeID { get; set; }
public int IngredientUniqID { get; set; }
}
public partial class Amount
{
public int AmountID { get; set; }
public string AmountName { get; set; }
}
public partial class Ingredient
{
public int IngredientID { get; set; }
public string IngredientName { get; set; }
}
public class DisplayRecipe
{
public string Name { get; set; }
public string Ingredient { get; set; }
public string Amount { get; set; }
public int DishID { get; set; }
}
}
The problem is that any of the b, c, t variables can be null due to DefaultIfEmpty and you need to account for that in any member access, including join conditions.
If you are using C#6 (VS2015), you can use ?. operator like this
var test = from dish in dishes
join ing in ingredients on dish.IngredientID equals ing.IngredientID into result
from b in result.DefaultIfEmpty()
join i in ingredient on b?.IngredientTypeID equals i.IngredientID into r
from c in r.DefaultIfEmpty()
join am in amount on b?.AmountID equals am.AmountID into s
from t in s.DefaultIfEmpty()
select new DisplayRecipe { Name = dish.DishName, Amount = t?.AmountName, Ingredient = c?.IngredientName, DishID = dish.DishID };
while in pre C#6:
var test = from dish in dishes
join ing in ingredients on dish.IngredientID equals ing.IngredientID into result
from b in result.DefaultIfEmpty()
join i in ingredient on b != null ? b.IngredientTypeID : null equals i.IngredientID into r
from c in r.DefaultIfEmpty()
join am in amount on b != null ? b.AmountID : null equals am.AmountID into s
from t in s.DefaultIfEmpty()
select new DisplayRecipe { Name = dish.DishName, Amount = t != null ? t.AmountName : null, Ingredient = c != null ? c.IngredientName : null, DishID = dish.DishID };
Problem is you added this line:
dishes.Add(new Dishes { DishID = 8, DishName = "Name", DishTypeID = 2, IngredientID = 2 });
But did not also add the other lines that are dependent on your join (example):
ingredients.Add(new Ingredients { AmountID = 2, IngredientID = 2, IngredientTypeID = 1, IngredientUniqID = 1 });
ingredient.Add(new Ingredient { IngredientID = 2, IngredientName = "ham" });
So when your program tries to find an ingredientID of 2 because that has been added to dishes it does not find one and produces an error.
Sample of code that works:
dishes.Add(new Dishes { DishID = 1, DishName = "Soup", DishTypeID = 1, IngredientID = 1 });
dishes.Add(new Dishes { DishID = 8, DishName = "Name", DishTypeID = 2, IngredientID = 2 });
ingredients.Add(new Ingredients { AmountID = 2, IngredientID = 1, IngredientTypeID = 1, IngredientUniqID = 1 });
ingredients.Add(new Ingredients { AmountID = 2, IngredientID = 2, IngredientTypeID = 1, IngredientUniqID = 1 });
amount.Add(new Amount { AmountID = 2, AmountName = "1 cup" });
ingredient.Add(new Ingredient { IngredientID = 1, IngredientName = "egg" });
ingredient.Add(new Ingredient { IngredientID = 2, IngredientName = "ham" });

Categories

Resources