I am working with some code that uses Columns.Add in conjunction with a lambda expression and would like to understand why/how it works. Here is a code snippet:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
if (items[0].ReportData1 != null)
{
if (items[0].ReportData1.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(items[0].ReportData1.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData1.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(items[0].ReportData1.Name);
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(items[0].ReportData1.Name);
}
}
if (items[0].ReportData2 != null)
{
if (items[0].ReportData2.DecimalValue != null)
{
Columns.Add(m => m.ReportData2.DecimalValue).Titled(items[0].ReportData2.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData2.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData2.IntValue != null)
{
Columns.Add(m => m.ReportData2.IntValue).Titled(items[0].ReportData2.Name);
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData2.StringValue).Titled(items[0].ReportData2.Name);
}
}
This method consists of code that repeats itself out to ReportData6, changing only the ReportData field name with each repetition.
Here is the ReportRowDataContract class:
public class ReportRowDataContract
{
public ReportDataDataContract ReportData1 { get; set; }
public ReportDataDataContract ReportData2 { get; set; }
public ReportDataDataContract ReportData3 { get; set; }
public ReportDataDataContract ReportData4 { get; set; }
public ReportDataDataContract ReportData5 { get; set; }
public ReportDataDataContract ReportData6 { get; set; }
// an indexed property - for accessing report data fields by index
public ReportDataDataContract this[int i]
{
get
{
return new ReportDataDataContract[]
{
ReportData1,
ReportData2,
ReportData3,
ReportData4,
ReportData5,
ReportData6
}[i];
}
}
public int GetReportDataFieldCount()
{
return 6;
}
}
Unfortunately, I cannot change the structure of this class so I'm trying to convert the first code block into a method that loops. However, I'm stuck on what the Columns.Add is actually adding to.
Here is my code thus far:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
// get count of how many fields exist in ReportRowDataContract
int reportDataFieldCount = (new ReportRowDataContract()).GetReportDataFieldCount();
// create columns for grid for each field in ReportRowDataContract
//foreach (ReportRowDataContract item in items)
//{
int i = 0;
while (i < reportDataFieldCount)
{
AddGridColumn(items[0][i]);
i++;
}
//}
}
private void AddGridColumn(ReportDataDataContract reportColumn)
{
if (reportColumn != null)
{
if (reportColumn.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(reportColumn.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ?
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") :
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (reportColumn.SumValue || reportColumn.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = reportColumn.Name,
AvgValue = reportColumn.AvgValue,
DecimalValue = 0
});
}
}
else if (reportColumn.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(reportColumn.Name);
if (reportColumn.SumValue || reportColumn.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = reportColumn.Name,
AvgValue = reportColumn.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(reportColumn.Name);
}
}
}
In the AddGridColumn method the part that is problematic is m => m.ReportData1. It's not dynamic so the first loop through is fine but on the second loop through an exception is thrown: System.ArgumentException {"Column 'ReportData1.StringValue' already exist in the grid"}. I know the m.ReportData1 needs to be changed or the approach modified...just don't know how to go about it.
Edit #1: Disabled the foreach per VitezslavSimon's insight. The same exception message is being thrown.
Edit #2: Base class of grid (I think).
public class Grid<T> : GridBase<T>, IGrid where T : class
{
public Grid(IEnumerable<T> items);
public Grid(IQueryable<T> items);
public IGridColumnCollection<T> Columns { get; }
public bool DefaultFilteringEnabled { get; set; }
public bool DefaultSortEnabled { get; set; }
public virtual int DisplayingItemsCount { get; }
public bool EnablePaging { get; set; }
public string Language { get; set; }
public IGridPager Pager { get; set; }
public GridRenderOptions RenderOptions { get; set; }
public ISanitizer Sanitizer { get; set; }
public override IGridSettingsProvider Settings { get; set; }
public virtual void AutoGenerateColumns();
protected internal virtual IEnumerable<T> GetItemsToDisplay();
}
It seems you need to add columns only once there. Try to help it by declaring a flag there. It also depends how your grid component is working behind.
Your code with draft of proposed change:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
// get count of how many fields exist in ReportRowDataContract
int reportDataFieldCount = (new ReportRowDataContract()).GetReportDataFieldCount();
// create columns for grid for each field in ReportRowDataContract
bool flag = true;
foreach (ReportRowDataContract item in items)
{
int i = 0;
if (flag) {
while (i < reportDataFieldCount)
{
AddGridColumn(items[0][i]);
i++;
}
flag = false;
}
}
}
Related
I have an object below:
public class SubjectCategory : BaseModel
{
public decimal ParentSubjectCategoryId { get; set; }
public bool IsEnable { get; set; }
public virtual List<Subject>? Subjects { get; set; }
public virtual List<SubjectCategory>? ChildrenCategoris { get; set; }
public virtual SubjectCategory? ParentCategory { get; set; }
}
I get lists of subjectCategories from database (Picture Below).
I wrote a method that it adds ChildrenCategoris inside the categories which their ParentSubjectCategory Id is NULL or 0, but the problem is it is only works for the first level of tree!
public List<SubjectCategory> GetAllSubjectCategories()
{
var res = _subjectCategoryRepository.Select(new SubjectCategory {}).ToList();
List<SubjectCategory> newSubjectCategory = new List<SubjectCategory>();
foreach (var item in res)
{
if(item.ParentSubjectCategoryId != 0)
{
var a = newSubjectCategory.Where(sc => sc.Id ==
item.ParentSubjectCategoryId).FirstOrDefault();
if(a.ChildrenCategoris == null)
{
newSubjectCategory.Where(sc => sc.Id ==
item.ParentSubjectCategoryId).FirstOrDefault().ChildrenCategoris = new List<SubjectCategory>() { item};
}
else
{
newSubjectCategory.Where(sc => sc.Id == item.ParentSubjectCategoryId).FirstOrDefault().ChildrenCategoris.Add(item);
}
}
else
{
newSubjectCategory.Add(item);
}
}
return res;
}
But every child can have many ChildrenCategoris and their children can have many ChildrenCategoris as well and again.
the loop count is unknown.
how can I have a list with multiple children with C# ?
I am trying to set issetting flag to true , if child exists for a
parent.
//Class file
public class EopsModule
{
public int ID { get; set; }
public string ModuleCode { get; set; }
public string Description { get; set; }
public bool IsDefaultModule { get; set; }
public int? ParentID { get; set; }
public bool IsSetting { get; set; }
public List<EopsModule> Children { get; set; }
}
public IResponseResult GetApplicationSettingWithModule()
{
IResponseResult responseResult = new ResponseResult();
dynamic dynamic = new ExpandoObject();
try
{
var settingsDetails = _databaseManager.GetMultipleDataByJson(DatabaseStoredProcedures.spGetAllApplicationSetting.ToString()).Result;
var oObjectDeserializeObject = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(settingsDetails);
//get here all EopsModule in List<EopsModule>
var moduleTreeHierarchy = _eopsModuleManager.GetAllEopsModuleWithHierarchy().Result;
dynamic.settingsDetails = oObjectDeserializeObject;
dynamic.moduleTreeHierarchy = moduleTreeHierarchy;
string oModuleCode = string.Empty;
foreach (var item in oObjectDeserializeObject)
{
oModuleCode = item.moduleCode;
moduleTreeHierarchy.
Where(x => x.ModuleCode == oModuleCode).ToList().ForEach(x =>
{
x.IsSetting = true;
});
}
responseResult = Helper.Response.ResponseModel(dynamic, true, Helper.Constants.ApiSuccess, HttpStatusCode.OK, true);
}
catch (Exception)
{
responseResult = Helper.Response.ResponseModel(null, false, Helper.Constants.ApiFailure, HttpStatusCode.BadRequest, true);
}
return responseResult;
}
The loop i am iterating is working for parent level but , its not updating the value for child values,
wondering if it can be achieved by recursive function.
Please find the output with existing code :
Have you tried something like this? I did not pass it through compiler but you'll get the idea.
public UpdateModuleAndChildren(Module moduleTreeHierarchy) {
if(moduleTreeHierarchy.children != null && moduleTreeHierarchy.children.Count() > 0) {
moduleTreeHierarchy.children.forEach(x => { this.UpdateModuleAndChildren(x) });
module.IsSetting = true;
}
}
Let me know if it helps.
In your code you will just call this.UpdateModuleAndChildren(moduleTreeHierarchy)
i have table looks like below
ID | Reason | PrID
-----------------
1 abc null
2 dhe null
3 aerc 1
4 dwes 2
5 adfje 1
i have class
public class Reason
{
public int Id { get; set; }
public string Reson{ get; set; }
public List<SecondryReason> SecReason{ get; set; }
public int? PrimaryId { get; set; }
}
public class SecondryReason
{
public int Id { get; set; }
public string Reason { get; set; }
public int PrimaryReasonId { get; set; }
}
I want this to be displayed in hierarchy level
if the prid is Null need to treat this as the parent remaining all child
i am trying Linq and unable to achieve this
Suggest me how to do this in an easy way in linq
So: You have a list/enumerable of type , whereof the SecReason List property is null. Then, using linq you want a list, were the only the "root" reasons remain, but the Sub-reasons got put in the lists, but as type SecondaryReason?
If so, I found this way to do it (linq and foreach):
static IEnumerable<Reason> GetReasonsGrouped(List<Reason> reasons)
{
var result = reasons.Where(x => x.PrimaryId == null);
foreach (var item in result)
{
item.SecReason = reasons.Where(x => x.PrimaryId == item.Id)
.Select(x => new SecondryReason()
{ Id = x.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = item.Id
})
.ToList();
}
return result;
}
Or just linq, but harder to read:
var result = reasons.Where(x => x.PrimaryId == null)
.Select(x =>
{
x.SecReason = reasons.Where(r => x.PrimaryId == x.Id)
.Select(r => new SecondryReason()
{
Id = r.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = x.Id
})
.ToList();
return x;
});
Not sure if linq will be the best solution, here is my proposed changes and method to get an Hierarchy type:
public class Reason
{
public int Id { get; set; }
public string Reson { get; set; }
public List<Reason> SecReason { get; set; }
public int? PrimaryId { get; set; }
//Adds child to this reason object or any of its children/grandchildren/... identified by primaryId
public bool addChild(int primaryId, Reason newChildNode)
{
if (Id.Equals(primaryId))
{
addChild(newChildNode);
return true;
}
else
{
if (SecReason != null)
{
foreach (Reason child in SecReason)
{
if (child.addChild(primaryId, newChildNode))
return true;
}
}
}
return false;
}
public void addChild(Reason child)
{
if (SecReason == null) SecReason = new List<Reason>();
SecReason.Add(child);
}
}
private List<Reason> GetReasonsHierarchy(List<Reason> reasons)
{
List<Reason> reasonsHierarchy = new List<Reason>();
foreach (Reason r in reasons)
{
bool parentFound = false;
if (r.PrimaryId != null)
{
foreach (Reason parent in reasonsHierarchy)
{
parentFound = parent.addChild(r.PrimaryId.Value, r);
if (parentFound) break;
}
}
if (!parentFound) reasonsHierarchy.Add(r);
}
return reasonsHierarchy;
}
I have a Collection:
Collection<DateOfValues> collectionDateOfValues;
...
I get a DateOfValues Instance - lets say dateOfValuesNew and want to iterate over the collection and overwrite only values that are different.
public class DateOfValues
{
{
this.Values = new Collection<SomeValue>();
}
public int id { get; set;}
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
}
public class SomeValue
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
publi decimal StatusNumber { get; set; }
}
What I have done:
if (dateOfValuesNew != null)
{
foreach (var dateOfValues in collectionDateOfValues)
{
if (dateOfValues.Id == dateOfValuesNew.Id)
{
// Here Im sure to find the dateOfValues Instance I will work with.
}
}
}
But If I want to compare dateOfValues with dateOfValuesNew with foreach it is ugly and unreadable.
Is there any better way to do it?
The Start of DateOfValues can be changed. It is the easiest part - cause I can simply overwrite it.
The hard part is to compare SomeValue Collection. Every SomeValue can have changed Date and Status - this can be solved with overwriting too.
But SomeValue Collection can become new SomeValue or it can be deleted.
For example dateOfValues has 3 SomeValue in SomeValue Collection and dateOfValuesNew will have 4 or 2.
A bit tricky but this works:
class Program
{
static void Main(string[] args)
{
// Test
DateOfValues dov1 = new DateOfValues { Id = 1, Start = new DateTime(2011, 12, 01) };
dov1.AddSomeValue(1,"OK",2);
dov1.AddSomeValue(2,"Not OK",3);
dov1.AddSomeValue(3,"Not OK",4);
dov1.AddSomeValue(4,"Additional dov1",5);
DateOfValues dov2 = new DateOfValues { Id = 1, Start = new DateTime(2011, 12, 02) };
dov2.AddSomeValue(1, "OK", 2);
dov2.AddSomeValue(2, "Not OK", 4);
dov2.AddSomeValue(3, "OK", 1);
dov2.AddSomeValue(6, "Additional dov2", 15);
foreach (Tuple<SomeValue,SomeValue> difference in dov1.GetDifference(dov2))
{
if (difference.Item1 != null)
{
Console.WriteLine("Item1: Id:{0}; Status:{1}; Status Number:{2}",
difference.Item1.Id, difference.Item1.Status, difference.Item1.StatusNumber);
}
if (difference.Item2 != null)
{
Console.WriteLine("Item2: Id:{0}; Status:{1}; Status Number:{2}",
difference.Item2.Id, difference.Item2.Status, difference.Item2.StatusNumber);
}
Console.WriteLine("-------------------------------------------");
}
}
}
public class DateOfValues
{
public DateOfValues()
{
Values = new Collection<SomeValue>();
}
public int Id { get; set; }
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
public void AddSomeValue(int id, string status, decimal statusNumber)
{
Values.Add(new SomeValue{Date = this,Id = id,Status = status,StatusNumber = statusNumber});
}
public IEnumerable<Tuple<SomeValue, SomeValue>> GetDifference(DateOfValues other)
{
IEnumerable<SomeValue> notMatching = Values.Where(v => !other.Values.Any(o => v.Equals(o)))
.Union(other.Values.Where(v=> !Values.Any(o=> v.Equals(o)))).Distinct();
return notMatching
.GroupBy(x => x.Id)
.Select(x =>
new Tuple<SomeValue, SomeValue>(
x.FirstOrDefault(y => y.Date == this), x.FirstOrDefault(y => y.Date == other)));
}
}
public class SomeValue : IEquatable<SomeValue>
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
public decimal StatusNumber { get; set; }
public bool Equals(SomeValue other)
{
return other.Id == Id && other.Status == Status && other.StatusNumber == StatusNumber;
}
}
Output:
Item1: Id:2; Status:Not OK; Status Number:3
Item2: Id:2; Status:Not OK; Status Number:4
-------------------------------------------
Item1: Id:3; Status:Not OK; Status Number:4
Item2: Id:3; Status:OK; Status Number:1
-------------------------------------------
Item1: Id:4; Status:Additional dov1; Status Number:5
-------------------------------------------
Item2: Id:6; Status:Additional dov2; Status Number:15
-------------------------------------------
Edit
Alternative you could use an EqualityComparer:
public class DateOfValues
{
public DateOfValues()
{
Values = new Collection<SomeValue>();
}
public int Id { get; set; }
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
public void AddSomeValue(int id, string status, decimal statusNumber)
{
Values.Add(new SomeValue { Date = this, Id = id, Status = status, StatusNumber = statusNumber });
}
public IEnumerable<Tuple<SomeValue, SomeValue>> GetDifference(DateOfValues other)
{
var notMatching = Values.Except(other.Values, new SomeValueComparer())
.Union(other.Values.Except(Values,new SomeValueComparer()));
return notMatching
.GroupBy(x => x.Id)
.Select(x =>
new Tuple<SomeValue, SomeValue>(
x.FirstOrDefault(y => y.Date == this), x.FirstOrDefault(y => y.Date == other)));
}
}
public class SomeValueComparer : IEqualityComparer<SomeValue>
{
public bool Equals(SomeValue x, SomeValue y)
{
return
x.Id == y.Id &&
x.Status == y.Status &&
x.StatusNumber == y.StatusNumber;
}
public int GetHashCode(SomeValue obj)
{
return obj.GetHashCode();
}
}
public class SomeValue
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
public decimal StatusNumber { get; set; }
public override int GetHashCode()
{
return string.Format("{0}{1}{2}",Id,Status,StatusNumber).GetHashCode();
// or a better method to get a hashcode
}
}
I have this LINQ statement that tries to set the 1st element in the collection of string[]. But it doesn't work.
Below is the LINQ statement.
docSpcItem.Where(x => x.DocID == 2146943)
.FirstOrDefault()
.FinishingOptionsDesc[0] = "new value";
public string[] FinishingOptionsDesc
{
get
{
if (this._FinishingOptionsDesc != null)
{
return (string[])this._FinishingOptionsDesc.ToArray(typeof(string));
}
return null;
}
set { this._FinishingOptionsDesc = new ArrayList(value); }
}
What's wrong with my LINQ statement above?
Couple of things.. There are some problems with your get and set. I would just use auto properties like this..
public class DocSpcItem
{
public string[] FinishingOptionsDesc { get; set; }
public int DocID { get; set; }
}
Next for your linq statement, depending on the presence of an item with an id of 2146943 you might be setting a new version of the object rather than the one you intended. This should work..
[TestMethod]
public void Linq()
{
var items = new List<DocSpcItem>();
//2146943
for (var i = 2146930; i <= 2146950; i++)
{
items.Add(new DocSpcItem()
{ DocID = i
, FinishingOptionsDesc = new string[]
{ i.ToString() }
}
);
}
var item = items.FirstOrDefault(i => i.DocID == 2146943);
if (item != null)
{
item.FinishingOptionsDesc = new string[]{"The New Value"};
}
}
and
public class DocSpcItem
{
public string[] FinishingOptionsDesc { get; set; }
public int DocID { get; set; }
}