Convert DataTable to a nested object using LINQ - c#

I have a stored proc returning a datatable using a stored procedure. I am able to convert the it to an object using the following code
outgoingPaymentData.AsEnumerable().Select(x => new OutgoingPaymentApprovalDetails() { });
Here is my OutgoingPaymentApprovalDetails class
public class OutgoingPaymentApprovalDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public string DocumentId { get; set; }
public string DebtorName { get; set; }
public string Currency { get; set; }
public double Amount { get; set; }
public string Description { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Now, instead of a flat list, I need to add heirarchy, to select this one object to 3 objects.
Classes as under:
public class OriginatorDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public List<DocumentDetails> DocumentDetails { get; set; }
}
public class DocumentDetails
{
public string DocumentId { get; set; }
public List<TransactionDetails> TransactionDetails { get; set; }
}
public class TransactionDetails
{
public string Amount { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Basically, All Documents of a particular Originator have to be in the list of DocumentDetails and all TransactionDetails of a particular document have to be in that list.
One way is to create a dictionary and add stuff in it and finally create an object. I was wondering if there was a more abbreviated and efficient way to do something like this.
TIA

You can do the grouping of retrieved records of OutgoingPaymentApprovalDetails using Linq to create the nested object of OriginatorDetails collection.
see below code
var originalDetails = inputs.GroupBy(g => g.OriginatorId)
.Select(g => new OriginatorDetails()
{
OriginatorId = g.Key,
Name = g.First().Name,
DocumentDetails = g.GroupBy(d => d.DocumentId)
.Select(d => new DocumentDetails()
{
DocumentId = d.Key,
TransactionDetails = d.Select(t => new TransactionDetails()
{
DebitAccountNo = t.DebitAccountNo,
CreditAccountNo = t.CreditAccountNo,
Amount = t.Amount.ToString()
}).ToList()
})
.ToList()
});
Check the created https://dotnetfiddle.net/FCA7Qc to demostrate your scenario.

Try this code:
Basically you need to group 2 times, first time by OriginatorId and Name and then by DocumentId like this:
var result = list.GroupBy(c => new {c.OriginatorId, c.Name})
.Select(g => new OriginatorDetails()
{
Name = g.Key.Name,
OriginatorId = g.Key.OriginatorId,
DocumentDetails = g
.GroupBy(dd => dd.DocumentId)
.Select(dd => new DocumentDetails()
{
DocumentId = dd.Key,
TransactionDetails = dd.ToList()
.Select(td => new TransactionDetails()
{
Amount = td.Amount.ToString(),
CreditAccountNo = td.CreditAccountNo,
DebitAccountNo = td.DebitAccountNo
}).ToList()
}).ToList()
}).ToList();

Related

NEST: Find Best match using elastic search

I am trying to write a query to find the best match. I have an index with the structure below.
public class UserProfileSearch
{
public int Id { get; set; }
public int Sex { get; set; }
public int Age { get; set; }
public int MaritalStatus { get; set; }
public int CountryLivingIn { get; set; }
public double Height { get; set; }
public double BodyWeight { get; set; }
...
}
When I start my search I use different parameters. I get the search parameters as an object which has the structure below.
public class UserPreference
{
public int Id { get; set; }
public int FromAge { get; set; }
public int ToAge { get; set; }
public int FromHeight { get; set; }
public int ToHeight { get; set; }
public string MartialStatus { get; set; } // This will have id in comma separated form: 11,23,24..
public string CountriesLivingIn { get; set; } // This will also have id in comma separated form: 11,23,24..
public string Sexes { get; set; }
...
}
I am trying to achieve like below.
QueryContainer qCs = null;
userPartnerPreference.CountriesLivingIn.Split(",").ToList().ForEach(id =>
{
qCs |= new TermQuery { Field = "countryLivingIn ", Value = int.Parse(id) };
});
QueryContainer qSs = null;
userPartnerPreference.MartialStatus.Split(",").ToList().ForEach(id =>
{
qSs &= new TermQuery { Field = "maritalStatus", Value = int.Parse(id) };
});
var searchResults = await _elasticClient.SearchAsync<UserProfileSearch>(s => s
.Query(q => q
.Bool(b => b
.Must(qSs)
.Should(
bs => bs.Range(r => r.Field(f => f.Age).GreaterThanOrEquals(userPartnerPreference.FromAge).LessThan(userPartnerPreference.ToAge)),
bs => bs.Range(r => r.Field(f => f.Height).GreaterThanOrEquals(userPartnerPreference.FromHeight).LessThanOrEquals(userPartnerPreference.ToHeight)),
bs => bs.Bool(bsb=>bsb.Should(qCs))
)
)
)
);
I basically want to find the best match result based on the parameters passed ordered by highest number of fields matched. I'm new to elastic search so is this the way to do it?
Note: I have other fields that I need to match. There are around 15 field, which I am planning to have inside should like age and height.

ASP.NET Core populate selectlist with union result

var a = _context.Components.Select(c => c.Week).Union(_context.FinishGoods.Select(f => f.Week));
WeekFilter = new SelectList(a, "Week", "Week");
this does not work. What do i need to make the union result work on select list? I Tried ToList(), AsEnumerable()
this has worked
Entries = _con.Query<ExcelViewModel>(query).ToList();
var selectList = Entries
.Select(e => new
{
Id = e.ComponentWeek.Value.ToString(),
Name = e.ComponentWeek.Value.ToString()
}
).Union(Entries.Select(e => new
{
Id = e.FinishGoodWeek.Value.ToString(),
Name = e.FinishGoodWeek.Value.ToString(),
}));
WeekFilter = new SelectList(selectList, "Id", "Name");
EDIT
here are the objects to union they are basically the same in structure
public class FinishGood
{
public int Id { get; set; }
public int? Quantity { get; set; }
public int? Week { get; set; }
public int EntryId { get; set; }
public Entry Entry { get; set; }
}
public class Component
{
public int Id { get; set; }
public int? Quantity { get; set; }
public int? Week { get; set; }
public int EntryId { get; set; }
public Entry Entry { get; set; }
}
Please try this one it works fine.
var a = _context.Components.Select(c => new { Week = c.Week.Value })
.Union(_context.FinishGoods.Select(f => new { Week = f.Week.Value })).ToList();
WeekFilter = new SelectList(a, "Week" , "Week");

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

EF Core return specific columns from child tables

I don't want to use .include to get the entire child tables. I just need select columns.
public class ProjectTypeDTO {
public string Type { get; set; }
}
public class CourseDTO {
public string CourseCode { get; set; }
public string CourseTitle { get; set; }
}
public class ProjectDTO {
public int Id { get; set; }
public ProjectTypeDTO ProjectType { get; set; }
public CourseDTO Course { get; set; }
public string StartTerm { get; set; }
public DateTime SignOff { get; set; }
public DateTime StartDateTime { get; set; }
}
[HttpGet("getallprojects")]
public IActionResult GetAllProjects()
{
var projects = _context.Projects
.Select(p => new ProjectDTO
{
Id = p.Id,
ProjectType = { Type = p.ProjectType.Type },
Course = { CourseCode = p.Course.CourseCode, CourseTitle = p.Course.CourseTitle },
StartTerm = p.StartTerm,
SignOff = p.SignOff,
StartDateTime = p.StartDateTime,
}).ToList();
return Ok(projects);
}
This is throwing a "NotImplementedException: The method or operation is not implemented." error.
I've tested it as an anonymous function and it works.
var projects = _context.Projects
.Select(p => new
{
p.Id,
p.ProjectType.Type,
p.SignOff,
p.StartDateTime,
p.Course.CourseCode,
p.Course.CourseTitle,
p.StartTerm
}).ToList();
An anonymous type won't work for my app, since I need to be able to make changes to this data before it gets returned.
Based on other examples I've seen here and other sites, this looks correct. Could it be a bug?
I haven't seen that syntax for sub-objects before. eg:
ProjectType = { Type = p.ProjectType.Type }
I believe that should be:
ProjectType = new ProjectTypeDTO{ Type = p.ProjectType.Type }

Multiple collections in an object using Linq

I have following object
public class bizObj
{
public string name { get; set; }
public int p_id { get; set; }
public string acc_number { get; set; }
public string a_name { get; set; }
public string a_phone { get; set; }
public virtual product product { get; set; }
public virtual account account { get; set; }
}
Linq statment to get data from db is
public IEnumerable<bizObj> GetbizObj(int id)
{
var acs = (from c in db.p_account
where c.p_id==id
select new bizObj
{
name = c.p_name,
p_id = c.product.id,
acc_number=c.account.acc_number,
a_name = c.a_name,
a_phone = c.a_phone
});
return acs;
}
The above code is working fine but it is returning one collection. What I am trying to
get is that it has a collection of
{
name,
p_id
//than a second collection which has all the records that have same name ane p_id
{
acc_number,
a_name
a_phone
}
}
Please let me know how I can accomplish this using linq/lambda expression. Thanks
Question is unclear, but it looks like you're saying you want to group rows by name and p_id.
var query = acs.GroupBy(x => new { x.name, x.p_id })
.Select(g => new { g.Key.name, g.Key.p_id, Items = g.ToList() });

Categories

Resources