How to map child objects to parent from Linq - c#

I want to to make a list of the object MyProduct the problem is when i map the child objects to the parent object
My code looks like this
ShopDatabaseEntities db = new ShopDatabaseEntities();
var Result1 = (from Products in db.Products
join ProductProperties in db.ProductProperties on Products.ProductId equals ProductProperties.ProductId
join Properties in db.Properties on ProductProperties.PropertyId equals Properties.PropertyId
select new
{
Products.ProductName,
Products.ProductPrice,
ProductProperties.PropertyValue,
Properties.PropertyName,
ProductProperties.PropertyId
}).ToList();
List<MyProduct> lii = new List<MyProduct>();
foreach (var item in Result1)
{
MyProduct pp = new MyProduct();
pp = (from c in Result1
select new MyProduct { Name = item.ProductName }).First();
MyProperty e = new MyProperty();
e.PropertyName = item.PropertyName;
e.PropertyValue = item.PropertyValue;
pp.pros.Add(e);
lii.Add(pp);
}

It seems to me that this is more what you want:
var query =
from Products in db.Products
join ProductProperties in db.ProductProperties on Products.ProductId equals ProductProperties.ProductId
join Properties in db.Properties on ProductProperties.PropertyId equals Properties.PropertyId
select new
{
ProductProperties.PropertyValue,
Properties.PropertyName,
ProductProperties.PropertyId,
Products.ProductName,
Products.ProductPrice
};
List<MyProduct> lii =
query
.ToArray()
.GroupBy(x => new
{
x.ProductName,
x.ProductPrice
}, x => new
{
x.PropertyValue,
x.PropertyName,
x.PropertyId
})
.Select(x => new MyProduct()
{
Name = x.Key.ProductName,
pros = x
.Select(y => new MyProperty()
{
PropertyName = y.PropertyName,
PropertyValue = y.PropertyValue,
})
.ToList()
})
.ToList();
BTW, this is the code that you should have included in your question to make answering much easier:
public class ShopDatabaseEntities
{
public List<MyProduct> Products = new List<MyProduct>();
public List<MyProductProperty> ProductProperties = new List<MyProductProperty>();
public List<MyProperty> Properties = new List<MyProperty>();
}
public class MyProduct
{
public int ProductId;
public string Name;
public string ProductName;
public decimal ProductPrice;
public List<MyProperty> pros = new List<MyProperty>();
}
public class MyProductProperty
{
public int ProductId;
public int PropertyId;
public double PropertyValue;
}
public class MyProperty
{
public int PropertyId;
public string PropertyName;
public double PropertyValue;
}
Type defs help immensely.

Related

C# how to achieve 'group by' 'left join' and list inside a list selection in linq

var products = from P in product.GetAllAsync().Result
join PV in
_repositoryVariation.GetAllAsync().Result on P.Id equals PV.ProductId
into PVLJ
from PV in PVLJ.DefaultIfEmpty()
select new ProductwithVarientsGroupDto
{
Label = P.ProductName,
ProductId = P.Id,
Items = new List<ProdcutVariantsdto>() { new ProdcutVariantsdto { Label = P.ProductName.ToString() + (PV == null ? "" : " (" + PV?.VariantName.ToString() + ")"), ProductId = P.Id, ProdVariantId = PV?.Id > 0 ? PV?.Id : 0 } }
//here i need to add multiple variant records to Items, now it
// showing each item as a separate row data
};
var result = products.ToList();
return result;
The following query will work. After you left joined the data, just switch to client-evaluation (using AsEnumerable() or ToList()) and do the actual grouping with Linq. EF Core will not be able to do a Linq style grouping on its own and the data is being retrieved anyway, so there is no downside doing it client-side:
var products = (from p in context.Products
join v in
context.ProductVariations on p.Id equals v.ProductId
into g
from v in g.DefaultIfEmpty()
select new {product = p, variation = v})
.AsEnumerable() // <-- switch to client-evaluation
.GroupBy(g => g.product, g => g.variation)
.Select(g => new ProductwithVarientsGroupDto
{
Label = g.Key.ProductName,
ProductId = g.Key.Id,
Items = g.Select(v => new ProdcutVariantsdto()
{
Label = g.Key.ProductName + (v == null
? ""
: " (" + v.VariantName.ToString() + ")"),
ProductId = g.Key.Id,
ProdVariantId = v.Id > 0
? v.Id
: 0
}).ToList()
});
As #PanagiotisKanavos already mentioned in the comments, your original code first retrieves all entities from the database and then executes your query on them in memory. If you want to keep doing that, just replace context.Products with product.GetAllAsync().Result and context.ProductVariations with _repositoryVariation.GetAllAsync().Result. You could then also drop the AsEnumerable(), because you are already processing the query client-side.
Here is the fully working sample console project I used for testing:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class ProductVariation
{
public int Id { get; set; }
public int ProductId { get; set; }
public string VariantName { get; set; }
}
public class ProductwithVarientsGroupDto
{
public string Label { get; set; }
public int ProductId { get; set; }
public List<ProdcutVariantsdto> Items = new List<ProdcutVariantsdto>();
}
public class ProdcutVariantsdto
{
public string Label { get; set; }
public int ProductId { get; set; }
public int? ProdVariantId { get; set; }
}
public class Context : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductVariation> ProductVariations { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
#"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63031344")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasData(
new Product {Id = 1, ProductName = "Car"},
new Product {Id = 2, ProductName = "Bus"});
modelBuilder.Entity<ProductVariation>().HasData(
new ProductVariation {Id = 1, ProductId = 1, VariantName = "Minivan"},
new ProductVariation {Id = 2, ProductId = 1, VariantName = "Convertible"},
new ProductVariation {Id = 3, ProductId = 2, VariantName = "Public Transportation"},
new ProductVariation {Id = 4, ProductId = 2, VariantName = "Shuttle"});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var products = (from p in context.Products
join v in
context.ProductVariations on p.Id equals v.ProductId
into g
from v in g.DefaultIfEmpty()
select new {product = p, variation = v})
.AsEnumerable()
.GroupBy(g => g.product, g => g.variation)
.Select(g => new ProductwithVarientsGroupDto
{
Label = g.Key.ProductName,
ProductId = g.Key.Id,
Items = g.Select(v => new ProdcutVariantsdto()
{
Label = g.Key.ProductName + (v == null
? ""
: " (" + v.VariantName.ToString() + ")"),
ProductId = g.Key.Id,
ProdVariantId = v.Id > 0
? v.Id
: 0
}).ToList()
});
var result = products.ToList();
Debug.Assert(result.Count == 2);
Debug.Assert(result[0].ProductId == 1);
Debug.Assert(result[0].Items.Count == 2);
Debug.Assert(result[0].Items[0].ProdVariantId == 1);
}
}
}

Linq perform join with updated field

I have 2 classes A and B, in which I wish to perform a linq query returning the class A (with all its members) along with a field from B.
Here is the class in which I'm returning:
public partial class A
{
// fields in A
public int classID { get; set; }
public string mem_1A { get; set; }
public string mem_2A { get; set; }
public int points { get; set; }
// ...
}
Here is the class in which I'm joining against:
public partial class B
{
// fields in B
public int classID { get; set; }
public string mem_1B { get; set; }
public string mem_2B { get; set; }
public int points { get; set; }
// ...
}
I've attempted the following variations but I wasn't able to get it working.
var db = new MyDBContext();
IEnumerable<A> result1 =
from tblA in db.A
join tblB in db.B on tblA.classID equals tblB.classID
select tblA;
IEnumerable<A> result2 = db.A.Join(db.B, x => x.classID, y => y.classID,(x,y) => x);
I'm only get all the members in A, but my points variable in class A is empty. I know I need to perform an assignment from the points in B, but I don't know how to do. What is the proper way to do this?
You might try this:
var result1 =
from tblA in db.A
join tblB in db.B on tblA.classID equals tblB.classID
select new { ClassA = tblA, PointsOfB = tblB.points };
It will create an anonymous class that holds tblA and the points of tblB.
Update the current value:
var result1 =
(from tblA in db.A
join tblB in db.B on tblA.classID equals tblB.classID
select new { ClassA = tblA, PointsOfB = tblB.points }).ToArray(); // <-- note this....
foreach(var r in result1)
r.ClassA.points = r.PointsOfB;
IEnumerable<A> classAUpdated = result1.Select(a => a.ClassA);
Or in one line:
IEnumerable<A> classAList = db.A
.Join(db.B, a => a.classID, b => b.classID,(a,b) => new
{
ClassA = a,
PointsOfB = b.points
}).ToArray () // <---------- to persist it
.Select(ab =>
{
ab.ClassA.points = ab.PointsOfB;
return ab.ClassA;
});
But you should test it.
I assume that this is what you want
var result2 = db.A.Join(db.B, x => x.classID, y => y.classID,(x,y) =>
new{classA = x,
pontOfB = y.points });
if you want it in classA, try this
var result2 = db.A.Join(db.B, x => x.classID, y => y.classID,(x,y) =>
new classA {
classId = x.classId,
mem_1a = x.mem_1a,
mem_1b = x.mem_1b,
points = y.points });

linq query to create new objects

assumed i have a class hierarchy where basically every class consists of a property item (like string) and a property list item of the nested class:
public class Master
{
public String myName;
public List<Detail> myDetails;
}
public class Detail
{
public String myDetailDescription;
public List<DetailParts> parts;
}
public class DetailParts
{
public int PartNumber;
public String PartDescription;
}
Now i would like to have a linq query that gives me
an list of objects that consists of these elements
public class Result
{
public String myName; // from Master
public String myDetailDescription; // from Detail
public int PartNumber; // from DetailParts
}
It is a silly question, I know, but i cant find useful links...
[Edit #1]: Better code sample:
public class Master
{
public String myName;
public List<Detail> myDetails = new List<Detail>();
}
public class Detail
{
public String myDetailDescription;
public List<DetailParts> parts = new List<DetailParts>();
}
public class DetailParts
{
public int PartNumber;
public String PartDescription;
}
// List of Masters
List<Master> master = new List<Master>();
// Two detail parts for the Details
DetailParts dp1 = new DetailParts { PartDescription = "dp1" };
DetailParts dp2 = new DetailParts { PartDescription = "dp2" };
// once again two details for the Master
Detail d1 = new Detail { myDetailDescription = "d1" };
Detail d2 = new Detail{ myDetailDescription = "d2"};
// Assign the Parts
d1.parts.Add(dp1);
d1.parts.Add(dp2);
d2.parts.Add(dp1);
d2.parts.Add(dp2);
Master m1 = new Master { myName = "m1" };
Master m2 = new Master { myName = "m2" };
m1.myDetails.Add(d1);
m1.myDetails.Add(d2);
m2.myDetails.Add(d1);
m2.myDetails.Add(d2);
master.Add(m1);
master.Add(m2);
// given a value for `myName` and `myDetailDescription` i
// would be able to get a list with the `DetailParts` objects:
var t = master.Where(a => a.myName == "m2")
.Select(a => a.myDetails).FirstOrDefault()
.Where(b => b.myDetailDescription == "d1")
.Select(c => c.parts).FirstOrDefault();
You are looking for SelectMany:
List<Result> results = masterList
.SelectMany(m => m.myDetails
.SelectMany(d => d.parts
.Select(dp => new Result
{
PartNumber = dp.PartNumber,
myDetailDescription = d.myDetailDescription,
myName = m.myName
})))
.ToList();
A more readable version with LINQ's query syntax:
var resultQuery = from m in masterList
from d in m.myDetails
from dp in d.parts
select new Result
{
PartNumber = dp.PartNumber,
myDetailDescription = d.myDetailDescription,
myName = m.myName
};
List<Result> results = resultQuery.ToList();

ASP.NET MVC 5 Entity Join

I'm new in ASP, Entity and lambda expressions. How can I join two tables?
Route Model:
public partial class Route
{
public Route()
{
Flights = new HashSet<Flight>();
}
public int RouteID { get; set; }
public int DepartureAirportID { get; set; }
public int ArrivalAirportID { get; set; }
public int FlightDuration { get; set; }
public virtual Airport Airport { get; set; }
public virtual Airport Airport1 { get; set; }
public virtual ICollection<Flight> Flights { get; set; }
}
Airport Model:
public partial class Airport
{
public Airport()
{
Routes = new HashSet<Route>();
Routes1 = new HashSet<Route>();
}
public int AirportID { get; set; }
public string City { get; set; }
public string Code { get; set; }
public virtual ICollection<Route> Routes { get; set; }
public virtual ICollection<Route> Routes1 { get; set; }
}
SQL query looks like this:
SELECT a.AirportID, a.City
FROM Route r INNER JOIN Airport a ON r.ArrivalAirportID = a.AirportID
WHERE r.DepartureAirportID = #departureAirportID
ORDER BY a.City
Sorry for this easy question but I don't know how to do this with Entity Framework...
Something like this should do (untested and just going on from your query) with a variable hard-coded):
using (var db = new YourDbContext())
{
var query = from r in db.Route
join a in db.Airport a on r.ArrivalAirportID equals a.AirportID
where r.DepartureAirportID = 1 // replace with your varialble.
orderby a.City
select a;
}
Include with join entity framework. here doctorSendAnswerModel also a inner table.
var data = _patientaskquestionRepository.Table.Include(x=>x.DoctorSendAnswer).Join(_patientRepository.Table, a => a.PatientId, d => d.Id, (a, d) => new { d = d, a = a }).Where(x => x.a.DoctorId == doctorid);
if(!string.IsNullOrEmpty(status))
data=data.Where(x=>x.a.Status==status);
var result = data.Select(x => new {x= x.a,y=x.d }).ToList();
var dt = result.Select(x => new PatientAskQuestionModel()
{
PatientId = x.x.PatientId.Value,
AskQuestion = x.x.AskQuestion,
Id = x.x.Id,
DoctorId = x.x.DoctorId,
FileAttachment1Url = x.x.FileAttachment1,
DocName = x.y.FirstName + " " + x.y.LastName,
CreatedDate = x.x.CreatedDate.Value,
doctorSendAnswerModel = x.x.DoctorSendAnswer.Select(t => new DoctorSendAnswerModel { Answer = t.Answer }).ToList()
}).ToList();
return dt;
LinQ query:
from r in context.Route
join a in context.Airport
on r.ArrivalAirportID equals a.AirportID
WHERE r.DepartureAirportID = "value"
ORDER BY a.City
select a.AirportID, a.City
var balance = (from a in context.Airport
join c in context.Route on a.ArrivalAirportID equals c.AirportID
where c.DepartureAirportID == #departureAirportID
select a.AirportID)
.SingleOrDefault();
You can do the following:
var matches = from a in context.Airports
join r in context.Routes
on a.AirportID equals r.ArrivalAirportID
where r.DepartureAirportID = departureAirportID
order by a.City
select new
{
a.AirportID,
a.City
};
Entity query with conditional join with pagination.
if (pageIndex <= 0)
pageIndex = 1;
pageIndex = ((pageIndex - 1) * pageSize) ;
var patient = _patientRepository.Table.Join(_DoctorPatient.Table.Where(x => x.DoctorId == Id && x.IsBlocked==false), x => x.Id, d => d.PatientId, (x, d) => new { x = x });
if (state != "")
patient = patient.Where(x => x.x.State.Contains(state));
if (name != "")
patient = patient.Where(x => (x.x.FirstName + x.x.LastName).Contains(name));
if (sdate != null)
patient = patient.Where(x => x.x.CreatedDate >= sdate);
if (eDate != null)
patient = patient.Where(x => x.x.CreatedDate <= eDate);
var result = patient.Select(x => x.x).Select(x => new PatientDoctorVM() { PatientId = x.Id, Id = x.Id, FirstName = x.FirstName, LastName = x.LastName, SSN = x.NewSSNNo, UserProfileId = x.UserProfileId, Email = x.Email, TumbImagePath = x.TumbImagePath }).OrderBy(x => x.Id).Skip(pageIndex).Take(pageSize).ToList();
Your entity and lembda query will be lool like this:
return (from d in _doctorRepository.Table
join p in _patientDoctor.Table on d.Id equals p.DoctorId
where p.PatientId == patientid.Value select d
).ToList();
Take a look at this site, it will explain you how the join works in Linq.
So if you ever need it again you will be able to solve it yourself.

Sum of amount column With LINQ (two tables)

I've two Tables lets simply say :
Products : ProductID, ProductName
Orders : OrderID,ProductID,Amount,Status
I want to write a LINQ query in C# which would
select ProductName,Sum(Amount) where Status = 1
I am stuck with this simple query :(
class Program
{
class Product
{
public int ProductID { get; set; }
public string ProductName {get; set; }
public Product(int ProductID, string ProductName)
{
this.ProductID = ProductID;
this.ProductName = ProductName;
}
}
class Order
{
public int OrderID { get; set; }
public int ProductID { get; set; }
public decimal Amount { get; set; }
public int Status { get; set; }
public Order(int OrderID, Product product, decimal Amount, int Status)
{
this.OrderID = OrderID;
this.ProductID = product.ProductID;
this.Amount = Amount;
this.Status = Status;
}
}
private static Product[] Products;
private static Order[] Orders;
static void Main(string[] args)
{
Products = new Product[]
{
new Product(1, "Bolt"),
new Product(2, "Nut"),
new Product(3, "Mounting Plate A"),
new Product(4, "Mounting Plate B")
};
Orders = new Order[]
{
new Order(1, Products[0], 1.12M, 0),
new Order(2, Products[1], 0.66M, 1),
new Order(3, Products[2], 4.12M, 0),
new Order(4, Products[0], 1.11M, 1),
new Order(5, Products[1], 0.67M, 1)
};
var results = from p in Products
join o in Orders on p.ProductID equals o.ProductID
where o.Status == 1
group o by p
into orderTotals
select new {ProductName = orderTotals.Key.ProductName, TotalAmount = orderTotals.Sum(o => o.Amount)};
foreach(var result in results)
{
Console.WriteLine("{0}: {1}", result.ProductName, result.TotalAmount);
}
}
}
Output:
Bolt: 1.11
Nut: 1.33
Although not the optimal, but this should work:
var interim =
from o in orders
where o.Status == 1
select new { o.OrderID, o.ProductID, o.Amount, o.Status};
var final =
from p in products
join x in interim on p.ProductID equals x.ProductID into g
select new { p.ProductName, Total = g.Sum(y => y.Amount) };

Categories