Viewbag is only grabbing information from final iteration of loop - c#

I'm trying loop through a list and put the contents into a viewbag. My problem is that when I grab the viewbag in the view, it's only showing me the contents from the final iteration of the loop. Any ideas on what is happening? Here is an example:
var output = new List<AirportsInACity>();
foreach (var airport in cities)
{
var temp = new AirportsInACity();
temp.airportName = airport.name
temp.aircraftList = new List<AirportAircraftTypeList>();
foreach (aircraft in airport.aircraftTypes)
{
aircraftTemp = new AirportAircraftTypeList();
aircraftTemp.aircraftType = aircraft.AircraftType.AircraftTypeModel;
aircraftTemp.seats = aircraft.Seats;
aircraftTemp.attendants = aircraft.Attendants;
temp.aircraftList.Add(aircraftTemp);
}
output.Add(temp);
var outputStats = temp.AircraftList.GroupBy(p => p.aircraftType);
foreach (var item in outputStats)
{
ViewBag.AirportName = temp.name;
ViewBag.AirportAircraftType = item.Key;
ViewBag.AircraftSeatsSum = item.Sum(p => p.seats);
}
}
ViewBag.Output = output;
return PartialView(output);
Now, lets say I have this information:
Airport Name: JFK
Aircraft Type: Boeing777
Total Seats: 400
Airport Name: ATL
Aircraft Type: Boeing777
Total Seats: 800
Airport Name: LAS
Aircraft Type: Boeing727
TotalSeats: 150
The problem is that in my Viewbag, it's only grabbing the last chunk of information (the LAS airport) but the console is outputting all of the information. What could the issue be?
Here is an example of what the view looks like:
#foreach (var item in Viewbag.Output)
{
<td>#(ViewBag.AirportName)</td>
<td>#(ViewBag.AirportAircraftType)</td>
<td>#(ViewBag.AircraftSeatsSum)</td>
}

On ViewBag.Output you have a list of type AirportsInACity with these properties: airportName and aircraftList witch is a list of type AirportAircraftTypeList. Next you have a foreach into `outputStat' that setting always the SAME ViewBag item.
You must create a new viewmodel where put the outputStats
public class AircraftViewModel
{
public string AirportName {get; set; }
public string AirportAircraftType {get; set;}
public int AircraftSeatsSum {get; set; }
}
[..]
var outputStats = temp.AircraftList.GroupBy(p => p.aircraftType);
List<AircraftViewModel> vm = new List<AircraftViewModel>();
foreach (var item in outputStats)
{
var aircraft = new AircraftViewModel();
aircraft.AirportName = temp.name;
aircraft.AirportAircraftType = item.Key;
aircraft.AircraftSeatsSum = item.Sum(p => p.seats);
vm.Add(aircraft);
}
}
ViewBag.Output = vm;

Create ExpandoObject and add it to the Details List.
Example:
Controller Code:
List<dynamic> Details = new List<dynamic>();
foreach (var item in outputStats)
{
dynamic detail;
detail = new ExpandoObject();
detail.AirportName =temp.name;
detail.AirportAircraftType = item.Key;
detail.AircraftSeatsSum =item.Sum(p => p.seats);
Details.Add(detail);
}
ViewBag.Details = Details;
View Code:
#foreach (dynamic detail in ViewBag.Details)
{
<td>#(detail.AirportName)</td>
<td>#(detail.AirportAircraftType)</td>
<td>#(detail.AircraftSeatsSum)</td>
}

Related

accessing json property with given values

var orderJson = JsonConvert.DeserializeObject<dynamic>(httpResultStr);
var orderidCount = orderJson.data.orderUuids.Count;
for (int i = 0; i <= orderidCount; i++)
{
var orderId = orderJson.data.orderUuids[i]; // my fail attempt. Didnt work
var map = orderJson.data.ordersMap;
foreach (var d in map)
{
var receipt = d.fareInfo.totalPrice;
Console.WriteLine(receipt);
}
}
Im trying to access the ordersMap members with the given values in orderUuids object. Inside the ordersMap Ids contain the fareInfo.totalPrice property that I'm trying to access. How would I go about achieving this?
[![json tree with ordersMap. Trying to access its members with the given values in orderUuids object.][1]][1]
You can make a partial/full map using the JSON file and use JsonConvert.DeserializeObject<>(json).
Other solution could be create a partial map using an anonymous type. Here is a code snip.
var anonymousTypeObject = new
{
status = "",
data = new
{
ordersMap = new Dictionary<string, JToken>(),
orderUuids = new string[0]
}
};
var obj = JsonConvert.DeserializeAnonymousType(json, anonymousTypeObject);
foreach (var kvp in obj.data.ordersMap)
{
var totalPrice = kvp.Value["fareInfo"]?["totalPrice"]?.ToString();
Debug.WriteLine($"{kvp.Key} -> {totalPrice}");
}
EDIT If you don't want any map use this solution.
var jObj = JsonConvert.DeserializeObject<JObject>(json);
var orderUuids = jObj.SelectToken("data.orderUuids")?.Values<string>();
foreach (var orderUuid in orderUuids)
{
var totalPrice = jObj.SelectToken($"data.ordersMap.{orderUuid}.fareInfo.totalPrice")?.Value<double>();
Debug.WriteLine($"{orderUuid} -> {totalPrice}");
}

How to load multiple lists in a datagridview

I currently work on a Windows Forms application and I have 3 lists of data and I want to add every list to a column of a datagrid. Is there a way how I can do this.
XDocument doc = XDocument.Load(Globals.pathNotifFile);
var dates = doc.Descendants("Date");
var hours = doc.Descendants("Time");
var message = doc.Descendants("Message");
var hoursCollection = new List<String>();
var dateCollection = new List<String>();
var messageCollection = new List<String>();
foreach (var date in dates)
{
dateCollection.Add(date.Value);
}
foreach (var hour in hours)
{
hoursCollection.Add(hour.Value);
}
foreach (var messages in message)
{
messageCollection.Add(messages.Value);
}
return Tuple.Create(hoursCollection,dateCollection, messageCollection);
}
The easiest way to accomplish this task is to build one object which contains your three datapoints. For example:
public class MyGridDateTime
{
public string Hour{get;set;}
public string Date{get;set;}
public string Message{get;set;}
}
public void InitalizeGrid()
{
List<MyGridDateTime> list = new List<MyGridDateTime>();
int i = 0;
foreach (string hour in hoursCollection)
{
list.Add(new MyGridDateTime {Hour = hour, Date = dateCollection[i], Message = messageCollection[i]};
i++;
}
grid.DataSource = list;
}
Note this only works if all of your Lists contain similiar amount of data. Else you need to update this a bit to become string.empty instead of Exception if your Lists are not of the same size.

How to add object to database from an IEnumerable collection?

I have a collection of ienumerable entities to be added to the database but it seems some conversion is needed. Can anyone point me in the right direction?
bool InsertDetails(DataTable detailTable, string fileName)
{
using (SunseapEBTContext context = new SunseapEBTContext())
{
if (InsertMaster(fileName))//if creating master record successful
{
int masterId = GetSPReadingM(m => m.FileName == fileName).SPReadingMasterId; //get MasterID of file uploaded
var details = detailTable.AsEnumerable().Select(row => new LeasingSPReadingDetailEntity()//new entity from datatable
{
//SPReadingId = row.Field<long>("ProductID"),
SPReadingMasterId = masterId,
BillCycleYear = int.Parse(row.Field<int>("Bill Cycle").ToString().Substring(0, 4)),
BillCycleMonth = byte.Parse(row.Field<byte>("Bill Cycle").ToString().Substring(4))
});
foreach(IEnumerable<LeasingSPReadingDetailEntity> detail in details)
{
context.LeasingSPReadingDetailEntities.AddObject(detail);
}
context.SaveChanges();
}
return true;
}
}
In the foreach loop, exception is thrown
CS1503 Argument 1: cannot convert from 'System.Collections.Generic.IEnumerable' to 'SunseapEBT.Domain.BillingModule.LeasingContract.Entity.LeasingSPReadingDetailEntity'
LeasingSPReadingDetailEntity class:
public class LeasingSPReadingDetailEntity
{
public long SPReadingId { get; set; }
public int SPReadingMasterId { get; set; }
public int BillCycleYear { get; set; }
public byte BillCycleMonth { get; set; }
}
More info:
A file which contains details is uploaded and will create a master record in one table. The details in the file are to be added to a separate table with the auto generated masterId from the first table. The issue is not being able to add the details into the database.
Edit:
I have found out why there was an error. The error was because of the contents of the file. Some rows had no values entered and the last row did not follow the format for the rest of the rows as it shows the total number of rows. Thanks all for the help!
#slawekwin comment is the answer. But I think there is a better solution, because it seems like your code is iterating 2x: 1st to generate new Enumerable (wasting memory), 2nd to add the object to context.
Might as well add the object directly when you iterate each rows.
foreach(var row in detailTable.AsEnumerable())
{
context.LeasingSPReadingDetailEntities.AddObject(
new LeasingSPReadingDetailEntity()//new entity from datatable
{
//SPReadingId = row.Field<long>("ProductID"),
SPReadingMasterId = masterId,
BillCycleYear = int.Parse(row.Field<string>("Bill Cycle").Substring(0, 4)),
BillCycleMonth = byte.Parse(row.Field<string>("Bill Cycle").Substring(4)),
AccountNumber = row.Field<string>("Account No."),
PeriodStart = row.Field<DateTime>("Period Start"),
PeriodEnd = row.Field<DateTime>("Period End"),
TownCouncil = row.Field<string>("Customer Name"),
Service = row.Field<string>("Service Type"),
Adjustment = row.Field<string>("Adjustment"),
Block = row.Field<string>("Blk"),
AddressLine1 = row.Field<string>("Adress Line 1"),
AddressLine2 = row.Field<string>("Adress Line 2"),
AddressLine3 = row.Field<string>("Postal Code"),
Usage = row.Field<decimal>("Usage"),
Rate = row.Field<decimal>("Rate"),
Amount = row.Field<decimal>("Amount")
}
);
}
--- EDIT ---
I am not sure, but I can guess that "Bill Cycle" field is neither int nor byte. Therefore, you should have retrieved it as string, then parse it to your new object. This is the part the I changed:
BillCycleYear = int.Parse(row.Field<string>("Bill Cycle").Substring(0, 4)),
BillCycleMonth = byte.Parse(row.Field<string>("Bill Cycle").Substring(4)),
You can change the problematic foreach loop to either of the below ones. I would prefer the first one as it is less verbose.
foreach(var detail in details)
{
context.LeasingSPReadingDetailEntities.AddObject(detail);
}
OR
foreach(LeasingSPReadingDetailEntity detail in details)
{
context.LeasingSPReadingDetailEntities.AddObject(detail);
}
If you look at the syntax of foreach loop, the very first construct variableType is type of the element being stored in the collection (LeasingSPReadingDetailEntity in your case) and NOT the type of the collection. You did the later which is why you are getting the invalid cast error.
foreach(variableType currentElementBeingIterated in collection){
//code block to operate on currentElement
}
Change it to List then add the item.
var details = detailTable.AsEnumerable().Select(row => new LeasingSPReadingDetailEntity()//new entity from datatable
{
SPReadingId = row.Field<long>("ProductID"),
SPReadingMasterId = masterId,
BillCycleYear = int.Parse(row.Field<int>("Bill Cycle").ToString().Substring(0, 4)),
BillCycleMonth = byte.Parse(row.Field<byte>("Bill Cycle").ToString().Substring(4)),
}).ToList();
foreach(var detail in details)
{
context.LeasingSPReadingDetailEntities.Add(detail);
}
or better still:
var details = detailTable.AsEnumerable().Select(row => new LeasingSPReadingDetailEntity()//new entity from datatable
{
SPReadingId = row.Field<long>("ProductID"),
SPReadingMasterId = masterId,
BillCycleYear = int.Parse(row.Field<int>("Bill Cycle").ToString().Substring(0, 4)),
BillCycleMonth = byte.Parse(row.Field<byte>("Bill Cycle").ToString().Substring(4)),
}).ToList();
context.LeasingSPReadingDetailEntities.AddRange(details);

Dynamics Crm: Get metadata for statuscode/statecode mapping

In Dynamics CRM 2011, on the Incident entity, the "Status Reason" optionset (aka statuscode) is related to the "Status" optionset (aka statecode)
e.g. see this screenshot
When I use the API to retrieve the Status Reason optionset, like so:
RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
{
EntityLogicalName = "incident",
LogicalName = "statuscode",
RetrieveAsIfPublished = true
};
RetrieveAttributeResponse attributeResponse = (RetrieveAttributeResponse)serv.Execute(attributeRequest);
AttributeMetadata attrMetadata = (AttributeMetadata)attributeResponse.AttributeMetadata;
StatusAttributeMetadata statusMetadata = (StatusAttributeMetadata)attrMetadata;
var dict = new Dictionary<int?, string>();
foreach (OptionMetadata optionMeta in statusMetadata.OptionSet.Options)
{
dict.Add(optionMeta.Value, optionMeta.Label.UserLocalizedLabel.Label);
}
It works in that I get the whole list of "Status Reason" (statuscode) options. However, I dont get any info about which "Status Reason" (statuscode) options relate to which "Status" (statecode) options.
How do I get that information?
You already have everything try insert this code inside of foreach:
int stateOptionValue = (int)((StatusOptionMetadata)optionMeta).State;
See StatusAttributeMetaData.OptionSet.Options hierarchy can return a type called StatusOptionMetadata if you use the State property of the StatusOptionMetadata, it will return the statecode this statuscode belongs to.
Here is how you can get it by querying the database
SELECT distinct e.LogicalName as entity,
smState.Value AS stateCode,
smstate.AttributeValue,
smStatus.Value AS [statuscode/statusreason],
smStatus.AttributeValue
FROM StatusMap sm
JOIN Entity e ON sm.ObjectTypeCode = e.ObjectTypeCode
JOIN StringMap smState ON smState.AttributeValue = sm.State
AND smState.ObjectTypeCode = e.ObjectTypeCode
AND smState.AttributeName = 'StateCode'
JOIN StringMap smStatus ON smStatus.AttributeValue = sm.Status
AND smStatus.ObjectTypeCode = e.ObjectTypeCode
AND smStatus.AttributeName = 'StatusCode'
WHERE e.LogicalName in ('lead')
ORDER BY e.LogicalName,
smState.AttributeValue,
smStatus.AttributeValue;
Here is working code that will output State/Status mapping for a given entity (you just need to provide the orgServiceProxy):
var dictState = new Dictionary<int, OptionMetadata>();
var dictStatus = new Dictionary<int, List<OptionMetadata>>();
string entityName = "lead";
int count=0;
using (var orgServiceProxy = GetOrgServiceProxy(orgServiceUriOnLine))
{
RetrieveAttributeResponse attributeResponse = GetAttributeData(orgServiceProxy, entityName, "statecode");
AttributeMetadata attrMetadata = (AttributeMetadata)attributeResponse.AttributeMetadata;
StateAttributeMetadata stateMetadata = (StateAttributeMetadata)attrMetadata;
foreach (OptionMetadata optionMeta in stateMetadata.OptionSet.Options)
{
dictState.Add(optionMeta.Value.Value,optionMeta);
dictStatus.Add(optionMeta.Value.Value,new List<OptionMetadata>());
}
attributeResponse = GetAttributeData(orgServiceProxy, entityName, "statuscode");
attrMetadata = (AttributeMetadata)attributeResponse.AttributeMetadata;
StatusAttributeMetadata statusMetadata = (StatusAttributeMetadata)attrMetadata;
foreach (OptionMetadata optionMeta in statusMetadata.OptionSet.Options)
{
int stateOptionValue = ((StatusOptionMetadata)optionMeta).State.Value;
var statusList = dictStatus[stateOptionValue];
statusList.Add(optionMeta);
count++;
}
}
Console.WriteLine($"Number of mappings: {count}");
foreach (var stateKvp in dictState.OrderBy(x=> x.Key))
{
Console.WriteLine($"State: {stateKvp.Value.Value}: {stateKvp.Value.Label.UserLocalizedLabel.Label}");
var statusList = dictStatus[stateKvp.Key];
Console.WriteLine($"\tStatuses");
foreach (var status in statusList.OrderBy(s => s.Value))
{
Console.WriteLine($"\t\t{stateKvp.Value.Value}: {status.Label.UserLocalizedLabel.Label}");
}
}

How to add a number of records into a List<T>

I have created an asp.net application using Entity Framework. In this I want to add the records into a list. For this I have to use the foreach loop but it always adding only last record data for all records, meaning it's showing same data. Here I have pasted my code. Please verify it once and guide where I can change.
public List<CategoryItems> ListMenuCategory(int MenuId)
{
string str = string.Empty;
string strJSON = string.Empty;
List<CategoryItems> resultmenu;
resultmenu = new List<CategoryItems>();
List<CategoryItems> Result;
Result = new List<CategoryItems>();
bool check = true;
var objmenuCategory = from cat in objEntity.menucategories where cat.MenuId == MenuId && cat.Active == check select cat;
CategoryItems Categorylist = new CategoryItems();
foreach (menucategory category in objmenuCategory)
{
Categorylist.CategoryName = category.CategoryName;
Categorylist.Description = category.Description;
int menuid = category.MenuCategoryId;
List<menuitem> menuitems = GetMenucategories(menuid);
foreach (var items in menuitems)
{
Categorylist.ItemName = items.ItemName;
Categorylist.Description = items.Description;
Categorylist.Price = (float)items.Price;
string Image = items.Picture;
Categorylist.Picture = "http://restaurantmanager.testshell.net/Images/" + Image;
Categorylist.Thumbnail = "http://restaurantmanager.testshell.net/Images/" + items.Thumbnail;
if (items.CreatedDate != null)
{
Categorylist.CreatedDate = (DateTime)items.CreatedDate;
}
if (items.ModifiedDate != null)
{
Categorylist.ModifiedDate = (DateTime)items.ModifiedDate;
}
Result.Add(Categorylist);
}
// Result.AddRange(menus);
}
return Result;
}
private List<menuitem> GetMenucategories(int p)
{
restaurantEntities objEntity1 = new restaurantEntities();
var menuitems = from items in objEntity1.menuitems where items.MenuCategoryId == p select items;
return menuitems.ToList();
}
You are creating the Categorylist item outside of the loops, so you are only using one single item, filling it with different data and adding it over and over to the result.
You have to create the item inside the innermost loop, so that each iteration gets its own object.
Note: ChrisF also spotted that you call AddRange inside the loop, which has the result that you will add the same set of items over and over. You don't need to call AddRange at all, you can just skip the Result list entirely and just return resultmenu instead.

Categories