Use LINQ to join 2 objects for each of its properties - c#

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
}

Related

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.

Strange Entity Framework behavior

I have 2 models:
public class Office
{
[Key] public int OfficeId { get; set; }
public string Brand { get; set; }
public string Type { get; set; }
[NotMapped] public IRepository<CarItem> CarsDataBase { get; set; }
public virtual ICollection<CarItem> Cars { get; set; }
public Office(string brand, string type)
{
Type = type;
Cars = new List<CarItem>();
Brand = brand;
}
}
and
public class CarItem
{
public CarItem() { } //for serialization
public CarItem(string brand, string model, uint price, uint stockBalance)
{
Brand = brand;
Model = model;
Price = price;
StockBalance = stockBalance;
}
[Key] public int ItemId { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
public uint Price { get; set; }
public uint StockBalance { get; set; }
public int? OfficeId { get; set; }
public Office Office { get; set; }
}
and DataBase Context
public class EFDataBaseContext : DbContext
{
public DbSet<Office> Offices => Set<Office>();
public DbSet<CarItem> CarItems => Set<CarItem>();
public EFDataBaseContext()
{
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=carstoredb;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Office>().HasData
(
new Office("Audi", "SQL")
{
OfficeId = 1,
},
new Office("Scoda", "SQL")
{
OfficeId = 2,
}
);
modelBuilder.Entity<CarItem>(entity =>
{
entity.HasOne(item => item.Office).WithMany(office => office.Cars).HasForeignKey(item => item.OfficeId).OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<SparePart>(entity =>
{
entity.HasOne(item => item.Office).WithMany(office => office.Parts).HasForeignKey(item => item.OfficeId).OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<CarItem>().HasData
(
new CarItem { OfficeId = 1, ItemId = 1, Brand = "Audi", Model = "A228", Price = 4, StockBalance = 14 },
new CarItem { OfficeId = 1, ItemId = 2, Brand = "Audi", Model = "Super", Price = 44, StockBalance = 5 },
new CarItem { OfficeId = 2, ItemId = 3, Brand = "Scoda", Model = "Logan", Price = 47, StockBalance = 9 },
new CarItem { OfficeId = 2, ItemId = 4, Brand = "Scoda", Model = "Spider", Price = 78, StockBalance = 3 }
);
}
Manually I added an office called BSU.
And in main function I write this:
using (EFDataBaseContext db = new EFDataBaseContext())
{
Office office = mainOffice.Dealerships.FirstOrDefault(of => of.Brand == "BSU");
CarItem carItem = new CarItem(office.Brand, "BSss", 2222, 4);
office.CarsDataBase ??= new EFDataBase(office);
office.CarsDataBase.Create(carItem);
}
Adding a new CarItem to the BSU somehow magically creates a new office named BSU in my database every time a new CarItem is added to the BSU.
using (EFDataBaseContext db = new EFDataBaseContext())
{
Office office = mainOffice.Dealerships.FirstOrDefault(of => of.Brand == "Audi");
CarItem carItem = new CarItem(office.Brand, "AuuuU", 2222, 4);
office.CarsDataBase ??= new EFDataBase(office);
office.CarsDataBase.Create(carItem);
}
Adding a new CarItem to an Audi, on the other hand, does absolutely nothing. No new cars with the Audi brand appear in the database, and nothing at all.
Seems like you're over complicating things
Adding a new Car to an existing Office should perhaps looks like:
var ctx = new EFDatabaseContext();
var off = ctx.Offices.FirstOrDefault(o => o.Type == "Main" and o.Brand == "Audi");
off.Cars.Add(new CarItem(off.Brand, "AuuuU", 2222, 4));
ctx.SaveChanges();
Find the office, add a car, save the changes

How can I join a list of employees and genders using linq

I have retrieve a list of employees. my employee class columns(employeeId, lastname, genderid)
List<m_employees> Items = new List<m_employees>
{
new m_employees{ employeeid = 1, lastname = "mike", genderid = 1 },
new m_employees{ employeeid = 2, lastname = "jeni", genderid = 2 }
};
then i have my gender class columns (id, title)
List<m_genders> genders = new List<m_genders>
{
new m_genders{ id = 1, title = "Male" },
new m_genders{ id = 2, title = "Female" }
};
//then i tried joining the retrieved list of employees to the genders
var x = from emp in Items
join sex in genders
on emp.genderid equals sex.id
into a from b in a.DefaultIfEmpty(new m_genders())
select new
{
emp.lastname,
emp.genderid,
sex = b.title
};
red error line is showing to the keyword join and says "the type of one of the expressions in the join clause is incorrect..."
how can i join them properly?
This happens because types emp.genderid, sex.id are different and you need to cast or convert them explicitly like that:
(int)emp.genderid equals sex.id
I was able to reproduce the error with the following class definition:
class m_genders
{
public int id { get; set; }
public string title { get; set; }
}
class employee
{
public int id;
public uint genderid;
public string lastname { get; set; }
}
Your question is not clear, the code work without any problem :
namespace WindowsFormsApp1
{
public class m_genders
{
public int id;
public string title;
}
public class m_employees
{
public int employeeid { get; set; }
public int genderid { get; set; }
public string lastname { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
List<m_genders> genders = new List<m_genders>
{
new m_genders {id = 1, title = "Male"},
new m_genders {id = 2, title = "Female"}
};
List<m_employees> Items = new List<m_employees>
{
new m_employees{ employeeid = 1, lastname = "mike", genderid = 1 },
new m_employees{ employeeid = 2, lastname = "jeni", genderid = 2 }
};
var x = from emp in Items
join sex in genders
on emp.genderid equals sex.id
into a
from b in a.DefaultIfEmpty(new m_genders())
select new
{
emp.lastname,
emp.genderid,
sex = b.title
};
}
}
}

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

C# linq group by multiple times

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.

Categories

Resources