How to do Multiple Left Join, Group By, and Concatenate in LINQ - c#

I am trying to display the 'names' of the dialects (from 'lu_dialect_t') of the Parents of a specific Child. I am doing multiple left joins with the LINQ query and now I am hoping to find a way on how to GROUP the query by the 'parent_id' and concatenate the 'name' (of dialects spoken by the parent) to one column and store it in a variable for my ViewModel.
This is my ViewModel:
public class ParentViewModel
{
public int parent_id { get; set; }
public string last_name { get; set; }
public string first_name { get; set; }
public string middle_name { get; set; }
public string ext_name { get; set; }
public Nullable<System.DateTime> birthdate { get; set; }
public string civil_status { get; set; }
public string email_address { get; set; }
public string cell_num { get; set; }
public string tel_num { get; set; }
public string fax_num { get; set; }
public string room_num_or_building { get; set; }
public string street { get; set; }
public string purok { get; set; }
public string subdivision { get; set; }
public Nullable<int> brgy_id { get; set; }
public string city_code { get; set; }
public string province_code { get; set; }
public string mother_tongue { get; set; }
public string educational_attainment { get; set; }
public string occupational_status { get; set; }
public string parent_type { get; set; }
public string deceased { get; set; }
public Nullable<System.DateTime> survey_date_conducted { get; set; }
public string person_who_conducted { get; set; }
public int child_id { get; set; }
public string parent_dialects { get; set; }
}
This is my Controller:
public ActionResult Parents(int id)
{ var query = (from p in db.parent_t
join cp in db.tn_child_parent_t on p.parent_id equals cp.parent_id into tcpGroup
from x in tcpGroup.DefaultIfEmpty()
join c in db.child_t on x.child_id equals c.child_id into cGroup
from y in cGroup.DefaultIfEmpty()
join pd in db.tn_parent_dialect_t on p.parent_id equals pd.parent_id into tpdGroup
from a in tpdGroup.DefaultIfEmpty()
join d in db.lu_dialect_t on a.dialect_id equals d.dialect_id into dGroup
from b in dGroup.DefaultIfEmpty()
where (y.child_id == id)
select new ViewModels.ParentViewModel
{
parent_id = p.parent_id,
last_name = p.last_name,
first_name = p.first_name,
middle_name = p.middle_name,
ext_name = p.ext_name,
birthdate = p.birthdate,
civil_status = p.civil_status,
email_address = p.email_address,
cell_num = p.cell_num,
tel_num = p.tel_num,
fax_num = p.fax_num,
room_num_or_building = p.room_num_or_building,
street = p.street,
purok = p.purok,
subdivision = p.subdivision,
brgy_id = p.brgy_id,
city_code = p.city_code,
province_code = p.province_code,
mother_tongue = p.mother_tongue,
educational_attainment = p.educational_attainment,
occupational_status = p.occupational_status,
parent_type = p.parent_type,
deceased = p.deceased,
survey_date_conducted = p.survey_date_conducted,
person_who_conducted = p.person_who_conducted,
parent_dialects = b.name,
});
return View(query);
}
Right now, the query just displays shows my table like this:
My current progress
But what I want is like this:
The desired result
Please help, I have been trying to find a way to do this for hours. Thank you.

Here is something similar
static void Main(string[] args)
{
var items = Enumerable.Range(0, 10).Select(p => new { Name = "Name" + p%2, LasetName = "LN"+p%2, Dialect = "D"+p });
var data = from item in items
group item by item.Name into g
select new
{
Name = g.Key,
LastName = g.First().LasetName,
Dialect = string.Join(",", g.Select(d=>d.Dialect))
}
;
foreach (var item in data)
{
Console.WriteLine($"Name:{item.Name}, Dialect:{item.Dialect}");
}
Console.WriteLine("Done");
Console.ReadLine();
}
Post process the var query with your group by and youse first for all the single properties you need. If you are using EF you will need to do a ToList first to get the data to memory for the concatenation. Also if there is a lot of data pulling all the rows in memory is not the best.

Related

Trouble understanding how to access nested list in list

I processing a soap response which the xml has a purchase order within items
eg
<PurchaseOrder>
<WHID>2</WHID>
<Supplier_ID>00</Supplier_ID>
<POID>6</POID>
<CreateDate>2013-01-02T10:48:27.37+11:00</CreateDate>
<CurrencyName>Australian Dollars</CurrencyName>
<ShippingStatus>Departed</ShippingStatus>
<payment_terms></payment_terms>
<shipping_terms></shipping_terms>
<POStatus>Back-Order</POStatus>
<PurchaseOrderItems>
<PurchaseOrderItem>
<SKU>Shoe30</SKU>
<Product_ID>124064</Product_ID>
<QtyOrdered>9</QtyOrdered>
<QtyOutstanding>6</QtyOutstanding>
<BuyPriceEx>20.0000</BuyPriceEx>
<DirectCosts>0.0000</DirectCosts>
<SupplierBuyPrice>20.0000</SupplierBuyPrice>
</PurchaseOrderItem>
</PurchaseOrderItems>
</PurchaseOrder>
I have no issues putting this into a jagged list . my classes look like this
public class PurchaseOrder
{
public string WHID { get; set; }
public string Supplier_ID { get; set; }
public string POID { get; set; }
public string CreateDate { get; set; }
public string CurrencyName { get; set; }
public string ShippingStatus { get; set; }
public string payment_terms { get; set; }
public string shipping_terms { get; set; }
public string POStatus { get; set; }
public List<PurchaseOrderItems> PurchaseOrderItems { get; set; }
}
public class PurchaseOrderItems
{
public string SKU { get; set; }
public string Product_ID { get; set; }
public string QtyOrdered { get; set; }
public string QtyOutstanding { get; set; }
public string BuyPriceEx { get; set; }
public string DirectCosts { get; set; }
public string SupplierBuyPrice { get; set; }
}
I fill the purchase order class using the following linq
List<PurchaseOrder> _orderDetailed = items.Select(po => new PurchaseOrder()
{
WHID = (string)po.Element("WHID").ElementValueNull(),
Supplier_ID = (string)po.Element("Supplier_ID").ElementValueNull(),
POID = (string)po.Element("POID").ElementValueNull(),
CreateDate = (string)po.Element("CreateDate").ElementValueNull(),
CurrencyName = (string)po.Element("CurrencyName").ElementValueNull(),
payment_terms = (string)po.Element("payment_terms").ElementValueNull(),
shipping_terms = (string)po.Element("shipping_terms").ElementValueNull(),
POStatus = (string)po.Element("POStatus").ElementValueNull(),
PurchaseOrderItems = po.Descendants("PurchaseOrderItem").Select(i => new PurchaseOrderItems()
{
SKU = (string)i.Element("SKU").ElementValueNull(),
Product_ID = (string)i.Element("Product_ID").ElementValueNull(),
QtyOrdered = (string)i.Element("QtyOrdered").ElementValueNull()
}).ToList()
}).ToList();
The problem come when I pass this to a reflection function that write the object to csv. it only writes the PurchaseOrder fields to the file. I have no idea how to access the PurchaseOrderItems fields so I can write them to the file.
I need to achieve the following using the above xml structure.
WHID Supplier_ID POID SKU Product_ID QtyOrdered
2 00 6 Shoe30 124064 6
I have cut down the fields above just to keep it easy to read. but the goal is to have all the line items and the purchase order header details on the one line.
public void WriteCSV<T>(IEnumerable<T> items, string path)
{
Type itemType = typeof(T);
var props = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.OrderBy(p => p.Name);
using (var writer = new StreamWriter(path))
{
writer.WriteLine(string.Join(fieldDelimiter, props.Select(p => p.Name)));
foreach (var item in items)
{
writer.WriteLine(string.Join(fieldDelimiter, props.Select(p => p.GetValue(item, null))));
}
}
}
I know I am missing how object work here so looking for some direction.
Much appreciated.
Instead of handling it in WriteCSV, you could pre-process the data to flatten (denormalize) it and then pass it to your existing WriteCSV:
var flatten = l.SelectMany(po => po.PurchaseOrderItems.Select(pi => new {
po.WHID,
po.Supplier_ID,
po.POID,
pi.SKU,
pi.Product_ID,
pi.QtyOrdered,
}));
WriteCSV(flatten);

Listview/Datagrid binding from query generated list

So I have a query that returns values from multiple tables with a left join.
But I can't seem to get the data from left join table.
public IEnumerable<TipsTricks> GetTipsTricks()
{
using(var connection = new SqlConnection(Connection.Instance.ConnectionString))
{
return connection.Query<TipsTricks>(#"SELECT tt.ID, cat.Omschrijving, tt.Info, tt.Onderwerp, tt.Firma FROM tblTipsAndTricks as tt
LEFT JOIN tblTT_Categorieen as cat on cat.Id = tt.CategorieID ");
}
}
I then do in code behind to bind it to Datagrid.ItemsSource:
public void initialize()
{
List<TipsTricks> tipstricks = DatabaseManager.Instance.TipsTricksRepository.GetTipsTricks().ToList();
DgTipsTricks.ItemsSource = tipstricks;
}
Class TipsTricks
public class TipsTricks
{
public int Id { get; set; }
public string Info { get; set; }
public string Onderwerp { get; set; }
public string Firma { get; set; }
string Omschrijving { get; set; }
}
Also tried the binding in de XAML without succes.
So I would like a column in the datagrid showing the content of cat.Omschrijving from the left join table tblTT_Categorieen.
Thanks!
Try making the property string Omschrijvin "public"
as shown below
public class TipsTricks
{
public int Id { get; set; }
public string Info { get; set; }
public string Onderwerp { get; set; }
public string Firma { get; set; }
public string Omschrijving { get; set; }
}

How can change a MYSQL Join Query in Linq Methods

I write a MySql join code, and want to retrive same value from the Dotnetcore linq methods.
My Join code is below:
SELECT GL.Id AS GradeLevels,
CRS.Name AS CourseName,
GL.Title AS GradlevelName,
AVG (ASTSTU.ObtainedMarks)
FROM GradeLevels GL
INNER JOIN Courses AS CRS ON CRS.GradeLevelsID = GL.Id
INNER JOIN Units AS UNT ON UNT.CourseID = CRS.ID
INNER JOIN Lessons AS LSN ON LSN.UnitsId = UNT.Id
INNER JOIN Assignments AS AST ON AST.LessonId = LSN.id
INNER JOIN AssignmentStudents AS ASTSTU ON ASTSTU.AssignmentId = AST.id
WHERE CRS.SchoolSystemsID = "08d6a1f2-26df-4ad5-25d3-2a26960aa3fd" -- School System id.
GROUP BY GL.Id;
Now I want to change above MySQL Join into Dotnet core linq method to create an API that will be Showing, I try to write code for this
public async Task<ICollection<GradeLevels>> GetSchoolSystemGradLevelsAverage(Guid schoolSystemId)
{
List<GradeLevels> dashboadOverAllAverage = new List<GradeLevels>();
var dashboadOverAllAverage1 = await _GpsContext.GradeLevels
.Include(d=>d.Departments)
.ThenInclude(c=>c.Courses.Where(s=>s.SchoolSystemsID ==schoolSystemId))
.ThenInclude(u=>u.Units)
.ThenInclude(l=>l.Lessons)
.ThenInclude(a=>a.Assignment)
.ThenInclude(a=>a.assignmentStudents)
.GroupBy(g=>g.ID)
.ToListAsync();
return dashboadOverAllAverage;
}
Now I want to show the data though API and want to call to fields GradeLvels name and Average Marks.
[HttpGet()]
public async Task<IActionResult> GetCEOGradeLevelAverage(string schoolSystemId)
{
var overallgradeAverages = await _ceoDashboadRepository.GetSchoolSystemGradLevelsAverage(Guid.Parse(schoolSystemId));
List<GetGradeLevelAverageVm> getOverallAverageVms = new List<GetGradeLevelAverageVm>();
foreach (GradeLevels overallgradeAverage in overallgradeAverages)
{
getOverallAverageVms.Add(new GetGradeLevelAverageVm
{
Marks = overallgradeAverage.Id.ToString(), //Want to show lable of AvrageMark
Name = overallgradeAverage.Name //Want to show Gradelevel name
});
}
return Ok(getOverallAverageVms);
}
You do select too much from your DB. Here an example, how to select the nessecary values:
using (TestDbContext ctx = new TestDbContext())
{
var tmp = ctx.AssignmentStudents
.Include(s => s.Assignment) // Include all Childs..
.ThenInclude(a => a.Lesson)
.ThenInclude(l => l.Unit)
.ThenInclude(u => u.Course)
.ThenInclude(c => c.GradeLevel)
.Where(a => a.LessonId == 123)
.GroupBy(g => // Group by your Key-Values Grade and Course (You could take names instead of ids. Just for simplification)
new
{
GradeLevel = g.Assignment.Lesson.Unit.Course.GradeLevel.Id,
Course = g.Assignment.Lesson.Unit.Course.Id
})
.Select(s => // Select the result into an anonymous type
new
{
GradeLevels = s.Key.GradeLevel, // with same keys like grouping
Course = s.Key.Course,
AverageObtainedMarks = s.Average(a => a.ObtainedMarks) // and an average ObtainedMarks from objects matching the key
})
.Where(s => s.GradeLevel == 1);
foreach (var t in tmp)
{
Console.WriteLine(t.GradeLevels + " " + t.Course + ": " + t.AverageObtainedMarks);
}
}
Here a the classes and dbcontext I used:
public class GradeLevel
{
public int Id { get; set; }
public List<Course> Courses { get; set; }
}
public class Course
{
public int Id { get; set; }
public int GradeLevelId { get; set; }
public GradeLevel GradeLevel { get; set; }
public List<Unit> Units { get; set; }
}
public class Unit
{
public int Id { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
public List<Lesson> Lessons { get; set; }
}
public class Lesson
{
public int Id { get; set; }
public int UnitId { get; set; }
public Unit Unit { get; set; }
public List<Assignment> Assignments { get; set; }
}
public class Assignment
{
public int Id { get; set; }
public int LessonId { get; set; }
public Lesson Lesson { get; set; }
public List<AssignmentStudent> AssignmentStudents { get; set; }
}
public class AssignmentStudent
{
public int Id { get; set; }
public int AssignmentId { get; set; }
public Assignment Assignment { get; set; }
public decimal ObtainedMarks { get; set; }
}
public class TestDbContext : DbContext
{
public DbSet<AssignmentStudent> AssignmentStudents { get; set; }
public DbSet<Assignment> Assignments { get; set; }
public DbSet<Lesson> Lessons { get; set; }
public DbSet<Unit> Units { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<GradeLevel> GradeLevels { get; set; }
}

Which type should I return?

Here i Have two tables with some columns.Here my aim is i want to do GroupBy operatio using ChilsMaster
public partial class Master
{
public int MasterId { get; set; }
public string Prod_Name { get; set; }
public string Produ_Adress { get; set; }
public Nullable<decimal> Price { get; set; }
}
public partial class ChildMasterMaster
{
public int ChildId { get; set; }
public Nullable<int> MasterId { get; set; }
public string SalesRec { get; set; }
public Nullable<bool> Prod_Deliver { get; set; }
}
public class Market_Masters
{
public int MasterId { get; set; }
public string Prod_Name { get; set; }
public string Produ_Adress { get; set; }
public Nullable<decimal> Price { get; set; }
public int ChildId { get; set; }
public string SalesRec { get; set; }
public Nullable<bool> Prod_Deliver { get; set; }
}
Here I write one private file which contains both columns of the table by using this join:
public IEnumerable<Market_Masters> GetMaster()
{
var x = from n in db.Masters
join chil in db.ChildMasterMasters on n.MasterId equals chil.MasterId into t
select new
{
n.MasterId,
n.Prod_Name,
n.Produ_Adress,
n.Price,
Hello = t
};
return ???;
}
If I write .ToList() it throws an Exception
You are currently returning an anonymous type defined by:
select new
{
n.MasterId,
n.Prod_Name,
n.Produ_Adress,
n.Price,
Hello = t
};
You cannot expose that as a strongly typed return type, precisely because it is anonymous. There is no Foo for which you can say "this is IEnumerable<Foo>".
You should probably create a named class that matches what you want, and return new YourNewType {...} (and return IEnumerable<YourNewType>)
You can't return anonymous types from a method (i.e. select new { ... }). You need to create a class for that or use Market_Masters if it is of that type, e.g.:
public IEnumerable<Market_Masters> GetMaster()
{
var x = from n in db.Masters
join chil in db.ChildMasterMasters on n.MasterId equals chil.MasterId into t
select new Market_Masters()
{
MasterId = n.MasterId,
Prod_Name = n.Prod_Name,
Produ_Adress = n.Produ_Adress,
Price = n.Price,
Hello = t
};
return x.ToList();
}
If the type returned is not Market_Masters you could do something like (replace YourChildType with your actual type):
public class MarketMastersWithHello : Market_Masters
{
public IEnumerable<YourChildType> Hello { get; set; }
}
and then:
public IEnumerable<MarketMastersWithHello> GetMaster()
{
var x = from n in db.Masters
join chil in db.ChildMasterMasters on n.MasterId equals chil.MasterId into t
select new MarketMastersWithHello()
{
MasterId = n.MasterId,
Prod_Name = n.Prod_Name,
Produ_Adress = n.Produ_Adress,
Price = n.Price,
Hello = t
};
return x.ToList();
}

EF4.1 - Attribute Evaluating to null at runtime

I'm using EF4.1 code first to create a simple database app with SQL CE 4 backend. I have a Product class and a CallItem class defined as so:
class CallItem
{
public int id { get; set; }
public float discount { get; set; }
public virtual Product Product { get; set; }
}
class Product
{
public int id { get; set; }
public decimal BaseCost { get; set; }
public int UnitSize { get; set; }
public bool isWasteOil { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Ingredients { get; set; }
}
edit - When I am creating a collection of CallItems using a LINQ query, I cannot access the attributes of the Product attached to each CallItem, eg
var callItems = from ci in context.CallItems select ci;
foreach(CallItem callItem in callItems)
{
RunSheet nrs = new RunSheet();
nrs.prodCode = callitem.Product.Code;
}
Interrogating the database shows that Productid in CallItems is being populated. However, the following line generates a NullReferenceException during run time:
nrs.prodCode = callitem.Product.Code;
Because callitem.Product is evaluating to null. Is this something to do with lazy loading and if so how can I resolve the issue?
RunSheet is another class, nrs is an instance whose attribute 'prodCode' I want to populate with the CallItem's Product's code.
Thanks!
From that code what you've showed it should work. Have you tried explicit loading?
var callItems = from ci in context.CallItems.Include(c => c.Product) select ci;
foreach(CallItem callItem in callItems)
{
RunSheet nrs = new RunSheet();
nrs.prodCode = callitem.Product.Code;
}
public class CallItem
{
public int Id { get; set; }
public float Discount { get; set; }
public virtual Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public decimal BaseCost { get; set; }
public int UnitSize { get; set; }
public bool IsWasteOil { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Ingredients { get; set; }
}
using (var context = new StackOverFlowContext())
{
var p = new Product
{
Id = 1,
BaseCost = 200,
Code = "Hola",
Description = "Soe description",
Ingredients = "Some ingredients",
IsWasteOil = true,
Name = "My Product",
UnitSize = 10
};
var item = new CallItem
{
Id = 101,
Discount = 10,
Product = p
};
context.CallItems.Add(item);
context.SaveChanges();
var result = from temp in context.CallItems
select temp;
Console.WriteLine("CallItem Id"+result.First().Id);
Console.WriteLine("ProductId"+result.First().Product.Id);
}
I wrote the above code with the following output
CallItemId 1
ProductId 1
The sql Profiler showed this
SELECT TOP (1)
[c].[Id] AS [Id],
[c].[Discount] AS [Discount],
[c].[Product_Id] AS [Product_Id]
FROM [dbo].[CallItems] AS [c]
It was too long for a comment ,so i put it here .

Categories

Resources