Formatting data in C# - c#

How would you format the following data outcome in C#.
My controller class returns the data like the one below; I used Dapper as ORM.
The problem here is it returns the number of teams times teams members (eg, Teams X Team Members)instead of team members within a team;
[HttpGet("GetMyTeamsDemo")]
public async Task <List<JTeam>> GetMyTeamsDemo(int UId)
{
List<JTeam> teams = new List<JTeam>();
JTeam jteam = new JTeam();
List<JMember> members = new List<JMember>();
var result = await _userDataStore.GetMyTeams(UId);
foreach (var item in result)
{
jteam.TeamId = item.Id;
jteam.TeamName = item.TeamName;
jteam.TeamsCode = item.TeamsCode;
jteam.Description = item.Description;
jteam.CreatedById = item.CreatedById;
jteam.CreatedByName = item.CreatedByName;
jteam.TeamProfilePhoto = item.TeamProfilePhoto;
jteam.CoverPhoto = item.CoverPhoto;
jteam.DateCreated = item.DateCreated;
var res = await _userDataStore.GetTeamMembersByTeamId(item.Id);
if (res is not null)
{
foreach (var item1 in res)
{
members.Add(new JMember { MemberId = item1.MemberId, MemberName = item1.MemberName, TeamId = item1.TeamId });
//teams.Members.Add(new JTeam.JMember { MemberId = itar.MemberId, MemberName = itar.MemberName, TeamId = itar.TeamId });
jteam.Members = members;
teams.Add(jteam);
}
}
jteam = new JTeam();
}
return teams;
}
public class JTeam
{
public int TeamId { get; set; }
public string TeamName { get; set; }
public string TeamsCode { get; set; }
public string Description { get; set; }
public int CreatedById { get; set; }
public string CreatedByName { get; set; }
public string TeamProfilePhoto { get; set; }
public string CoverPhoto { get; set; }
public DateTime DateCreated { get; set; }
public JMember Members { get; set; }
}
public class JMember
{
public int MemberId { get; set; }
public int TeamId { get; set; }
public string MemberName { get; set; }
}

foreach (var item1 in res)
{
...
jteam.Members = members;
teams.Add(jteam);
}
You are adding the team to the list within the foreach for the team members instead of after the members have been added to the team. It's the same object in memory, so when the JSON serializer loops through the "teams" it has multiple references to the same team and adds it multiple times to the JSON.
Move teams.Add(jteam); outside of the inner foreach loop (and the if that contains it).
I would also initialize jteam within the loop instead of defining it outside of the loop and resetting it at the end.

Related

How to insert into an enumerable list, which is an member of another list

I have an AcadamicFee list, at one point in time I want to insert an item under Fee. So that, it should add like Fee[1], Fee[2].modified the code like below.
I want to insert one new item under Fee list. But it didn't reflect. How to update the AcadamicFee list through Linq?
Thanks in advance.
AcadamicFee[0]
{
----
----
----
TermDetails [0]
{
---
Fee[0]
Fee[1]
Fee[2]
}
}
public class Fee
{
public int FeeID { get; set; }
public string FeeName { get; set; }
public string FeeAmount { get; set; }
public string IsFineApplicable { get; set; }
public string LastDate { get; set; }
}
public class Ter
{
public int TermID { get; set; }
public string TermName { get; set; }
public IEnumerable<Fee> FeeDetails { get; set; }
}
public class AcadamicFee
{
public int id { get; set; }
public string AdmissionID { get; set; }
public string StudentName { get; set; }
public int Class { get; set; }
public string AcadamicYear { get; set; }
public Nullable<int> TotalPaid { get; set; }
public Nullable<int> AcadamicAmount { get; set; }
public Nullable<int> BalanceAmount { get; set; }
public IEnumerable<Term> TermDetails { get; set; }
}
Any clue how to do with Linq? I tried something like this:
for (var i = 0; i < fees.Count; i++)
{
bool termDetailsExist = acadamic_fee.Any(a => a.TermDetails.Any(b => b.TermID == fees[i].TermID));
if (termDetailsExist)
{
foreach (AcadamicFeeModel acm in acadamic_fee)
{
foreach (TermModel trm in acm.TermDetails)
{
if (trm.TermID == fees[i].TermID)
{
List<FeeModel> FeeType = new List<FeeModel>();
FeeType.Add(new FeeModel
{
FeeID = fees[i].FeeID,
FeeName = fees[i].FeeName,
FeeAmount = fees[i].FeeAmount,
IsFineApplicable = fees[i].IsFineApplicable,
LastDate = fees[i].LastDate,
});
trm.FeeDetails.ToList().AddRange(FeeType);
}
}
}
}
}
trm.FeeDetails.ToList().AddRange(FeeType);
You've created a new List<FeeModel>, added a copy of the FeeDetails sequence, added a new FeeModel object, and then thrown the new list away.
You need to store the modified list in the property - for example:
foreach (TermModel trm in acm.TermDetails)
{
if (trm.TermID == fees[i].TermID)
{
List<FeeModel> FeeType = new List<FeeModel>();
FeeType.Add(new FeeModel
{
FeeID = fees[i].FeeID,
FeeName = fees[i].FeeName,
FeeAmount = fees[i].FeeAmount,
IsFineApplicable = fees[i].IsFineApplicable,
LastDate = fees[i].LastDate,
});
FeeType.AddRange(trm.FeeDetails);
trm.FeeDetails = FeeType;
}
}

How to loop thru a model and print without typing the name of properties

I have a Model that is filled with 20 Properties, for instance such as
public class SensorModel
{
public string Trigger1 { get; set; }
public string PathDoor1 { get; set; }
public string PathDoor2 { get; set; }
public string PathTrigger1 { get; set; }
public string PathTrigger2 { get; set; }
public string PathTrigger3 { get; set; }
public string PathTrigger4 { get; set; }
public string PathTrigger5 { get; set; }
public string PathTrigger6 { get; set; }
public string PathTrigger7 { get; set; }
public string PathTrigger8 { get; set; }
}
After declaring and setting their properties by doing such,
SensorModel sensorsData = new SensorModel();
How can I access sensorsData's properties using a loop?
Because I would like to logs all the data into a txt along with DateTime, I find manually accessing is a waste of time.
Is there any way to automate, for instance, using a loop and accessing it one by one?
You can use reflection to achieve your goal:
var model = new SensorModel() {
PathDoor1 = "Foo",
PathDoor2 = "Foo2",
PathTrigger1 = "Value of PT1",
PathTrigger2 = "Value of PT2",
};
foreach(var value in model.GetTriggerValues()) {
Console.WriteLine(value);
}
public class SensorModel
{
public string Trigger1 { get; set; }
public string PathDoor1 { get; set; }
public string PathDoor2 { get; set; }
public string PathTrigger1 { get; set; }
public string PathTrigger2 { get; set; }
/* ... */
public IEnumerable<string> GetTriggerValues() {
foreach(var prop in this.GetType().GetProperties().Where(x => x.Name.StartsWith("PathTrigger"))) {
yield return (string)prop.GetValue(this, null);
}
}
}
This example filters your properties by name, if you want or need a different result set, amend or remove the where clause.
You can use reflection to achieve this:
var obj = new SensorModel();
// ...
// Get all the properties of your class
var props = typeof(SensorModel).GetProperties();
foreach (var prop in props)
{
// Get the "Get" method and invoke it
var propValue = prop.GetGetMethod()?.Invoke(obj, null);
// Do something with the value
Console.Out.WriteLine("propValue = {0}", propValue);
}

Automapper override values which are not in source

Hi have a problem with auomapper where i try to use the Automapper.Mapper(src, dest) without loosing the values in the dest after doing the mapping.
I have a class which has a list of objects like below
public class UpdateShipmentDetailDto
{
public bool IsDocument { get; set; }
public List<UpdateItemDetailDto> ItemDetails { get; set; } = new();
}
which i want to map to
public class SCS_OUT_Manifest
{
public Guid ManifestId { get; set; }
public ICollection<SCS_OUT_ManifestItem> SCS_OUT_ManifestItems { get; set; } = new List<SCS_OUT_ManifestItem>();
}
The UpdateItemDetailDto class looks like this
public class UpdateItemDetailDto
{
public Guid ItemId { get; set; }
public string ItemDescription { get; set; }
public int Qty { get; set; }
public Guid UnitsId { get; set; }
public decimal ItemValue { get; set; }
}
And the SCS_OUT_ManifestItem class looke like
public class SCS_OUT_ManifestItem
{
public Guid ItemId { get; set; }
public Guid ManifestId { get; set; }
public string ItemDescription { get; set; }
public int Qty { get; set; }
public Guid UnitsId { get; set; }
public decimal ItemValue { get; set; }
}
Im performing a maaping like below, which map from ItemDetails (which is a list) to SCS_OUT_ManifestItems (which is also a ICollection).
_mapper.Map(updateShipmentDetailDto.ItemDetails, manifest.SCS_OUT_ManifestItems);
The problem after mapping is done the properties which in the destination collection are set to the default values.
for example the ManifestId inthe SCS_OUT_ManifestItem manifest.SCS_OUT_ManifestItems which is not in updateShipmentDetailDto.ItemDetails is set to its default Guid value 00000000-0000-0000-0000-000000000000.
But if i run this in a loop like below it works.
foreach (var item in manifest.SCS_OUT_ManifestItems)
{
_mapper.Map(updateShipmentDetailDto.ItemDetails.Single(s => s.ItemId == item.ItemId), item);
}
Please help! thanks in advance
Try map your lists and your itens and use the same name ÏtemDetails" in both lists.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<UpdateItemDetailDto, SCS_OUT_ManifestItem>();
cfg.CreateMap<UpdateShipmentDetailDto, SCS_OUT_Manifest>();
});
var _mapper = config.CreateMapper();
var updateShipmentDetailDto = new UpdateShipmentDetailDto();
var updateItemDetailDto = new UpdateItemDetailDto();
var manifest = new SCS_OUT_Manifest();
updateItemDetailDto.ItemId = Guid.NewGuid();
updateItemDetailDto.UnitsId = Guid.NewGuid();
manifest.ManifestId = Guid.NewGuid();
updateItemDetailDto.ItemDescription = "test";
updateItemDetailDto.Qty = 10;
updateItemDetailDto.ItemValue = 25.50M;
updateShipmentDetailDto.ItemDetails = new List<UpdateItemDetailDto>();
updateShipmentDetailDto.ItemDetails.Add(updateItemDetailDto);
_mapper.Map(updateShipmentDetailDto.ItemDetails, manifest.ItemDetails);
Console.WriteLine($"DTO Guid: {updateShipmentDetailDto.ItemDetails[0].ItemId}, Desc: {updateShipmentDetailDto.ItemDetails[0].ItemDescription}");
foreach (var item in manifest.ItemDetails)
{
Console.WriteLine($"Guid: {item.ItemId}, Desc: {item.ItemDescription}");
}
Console.WriteLine($"Guid Manifest: {manifest.ManifestId}");

Automapper: object of joined rows to header objects with a list of detail objects

I have a stored procedure that returns rows consisting of a join on Po header and Po Detail tables (so Po Header info is repeated in each child Po detail). My repository method maps this combined info into a Po object where each header info is repeated for every associated PO detail. Is there a best way to map these Po objects into a PO Header with a list of PO details?
Edit: I'm using Dapper for ORM
Second Edit: I added my repo method
public class Po
{
public string HeaderField1 { get; set; }
public string HeaderField2 { get; set; }
public int HeaderField3 { get; set; }
public string DetailField1 { get; set; }
public string DetailField2 { get; set; }
public int DetailField3 { get; set; }
}
to
public class Po_Header
{
public string HeaderField1 { get; set; }
public string HeaderField2 { get; set; }
public int HeaderField3 { get; set; }
List<Po_Detail> PoDetails { get; set; }
}
public class Po_Detail
{
public string DetailField1 { get; set; }
public string DetailField2 { get; set; }
public int DetailField3 { get; set;
}
Repo Method:
public IEnumerable<Po> GetAllPosByCustomer(string customerId)
{
try
{
using (var connection = (SqlConnection) _dbFactory.GetConnection())
{
var query = "dbo.spPo_GetAllByCustomerId";
var list = SqlMapper.Query<Po>(connection, query, param: new { #CustomerId = customerId }, commandType: CommandType.StoredProcedure);
return list;
}
}
catch(Exception e)
{
var err = e.ToString();
return null;
}
}

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

Categories

Resources