In a project there is a reporting class as follows:
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; }
}
Then there is a method that works with objects from the above class. Here is the first part of this method:
public ReportGrid(List<ReportRowDataContract> items , List<ReportDataDataContract> summaryData)
: base(items)
{
passedInSummaryData = summaryData;
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. Is there a way that this method can be rewritten to process each ReportData field by looping somehow? Besides making for a shorter method, this would be extremely useful to have in order to avoid manually updating the method if additional ReportData fields need to be added to the ReportRowDataContract class in the future.
Edit #1: I am fairly new to C# so detailed answers of how to go about this would be immensely helpful.
Edit #2: Thanks to Zohar Peled's post below, the following code feels very close. However, m.ReportData1 is causing problems in the AddGridColumn() method. The error message is 'ReportRowDataContract' does not contain a definition for 'item'...
I tried passing in ReportData 1 as a second argument when AddGridColumn() is called, but to no avail. Is there a way to modify the code so it works?
code that calls method:
// create columns for grid
AddGridColumn(items[0].ReportData1);
AddGridColumn(items[0].ReportData2);
AddGridColumn(items[0].ReportData3);
AddGridColumn(items[0].ReportData4);
AddGridColumn(items[0].ReportData5);
AddGridColumn(items[0].ReportData6);
method:
private void AddGridColumn(ReportDataDataContract item)
{
if (item != null)
{
if (item.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(item.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 (item.SumValue || item.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = item.Name,
AvgValue = item.AvgValue,
DecimalValue = 0
});
}
}
else if (item.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(item.Name);
if (item.SumValue || item.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = item.Name,
AvgValue = item.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(item.Name);
}
}
}
Edit #3: This is the ReportDataDataContract class definiton:
public class ReportDataDataContract
{
public string Name { get; set; }
public string StringValue { get; set; }
public decimal? DecimalValue { get; set; }
public int? IntValue { get; set; }
public bool SumValue { get; set; }
public bool AvgValue { get; set; }
public int? Index { get; set; }
public string DisplayFormat { get; set; }
}
Can you add an indexed property like this to ReportRowDataContract in order to essentially access the ReportData values as an indexed property?
public ReportDataDataContract this[int i]
{
get
{
return new ReportDataDataContract[] { ReportData1, ReportData2, ReportData3, ReportData4, ReportData5, ReportData6 }[i];
}
}
The you can use items[0][i].SumValue, for example, instead of items[0].ReportData1.SumValue.
If you can, you should simply change the ReportRowDataContract to only hold a single property that is an array of ReportDataDataContract. Then you can use a simple loop for each of the data contracts.
If you can't change the ReportRowDataContract table, you could use a method that takes in an argument of type ReportDataDataContract and have all the repeated code there - and just call it one time for each property.
The method should look something like that (based on the code you've posted):
void DoSomething(ReportDataDataContract dataContranct)
{
if (dataContranct != null)
{
if (dataContranct.DecimalValue != null)
{
Columns.Add(m => dataContranct.DecimalValue).Titled(dataContranct.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#dataContranct.DisplayFormat)) ?
Convert.ToDecimal(#dataContranct.DecimalValue).ToString("N") :
Convert.ToDecimal(#dataContranct.DecimalValue).ToString(#dataContranct.DisplayFormat));
if (dataContranct.SumValue || dataContranct.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = dataContranct.Name,
AvgValue = dataContranct.AvgValue,
DecimalValue = 0
});
}
}
else if (dataContranct.IntValue != null)
{
Columns.Add(m => dataContranct.IntValue).Titled(dataContranct.Name);
if (dataContranct.SumValue || dataContranct.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = dataContranct.Name,
AvgValue = dataContranct.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => mdataContranct.StringValue).Titled(dataContranct.Name);
}
}
}
Related
I'm trying to work out how to validate a deeply nested form structure (sections/panels/fields) where the fields themselves are stored as a dictionary. I have managed to fix up the paths and such so that the standard ValidationMessage componet displays a reasonable human readable error, and all of my validations do appear to function, unfortunately the Css Class setting has ceased to function.
When using a simple form layer (unnested Poco) the classes are applied automatically - my SyncFusion controls receive their 'e-error' or 'e-success' classes and change colour accordingly - when using a home made Validator, the colours don't function any more. I also tried setting my own CssClassProvider with EditContext.SetFieldCssClassProvider, and a breakpoint on the GetFieldCssClass function is never hit.
Effectively whilst my calculated FieldIdentifiers work correctly with ValidationMessage, that doesn't lead to any kind of Css Update.
Is there some kind of trigger that needs to be called from my FluentValidation Validator to kick off the CssClass mechanism?
Here's some code - note that the EditForm was in the parent page of PanelLayout and it's Model is of type PanelLayoutData - I didn't want to have to paste all of that.
PanelLayout.razor
<article class="st-vscroll bg-body pb-5 st-theme-#Theme">
<PanelLayoutValidator />
<ValidationSummary />
#{
int sectIndex = 0;
foreach(var panel in Data.panelData) {
int panIndex = 0;
while (panIndex < panel.Panels.Count)
{
var pan1 = panel.Panels[panIndex++];
<FieldListPanel Title=#pan1.Title DataDictionary=#(pan1.DisplayDictionary) LabelAbove=#true />
}
}
}
</article>
#code {
[CascadingParameter(Name = "Theme")] public string Theme { get; set; }
[CascadingParameter(Name = "EditMode")] public bool EditMode { get; set; }
[CascadingParameter] public EditContext EditContext { get; set; }
[Parameter] public clientmodels.PanelLayoutData Data { get; set; } = null;
protected override void OnParametersSet()
{
sectionRefs = new ElementReference[Data.panelData.Count];
if (EditContext != null) {
EditContext.SetFieldCssClassProvider(new _PanelLayoutFieldCssClassProvider());
}
}
}
FieldListPanel.razor
<div class="card rounded-0 border-0">
#if (!string.IsNullOrEmpty(Title))
{
<div class="card-header border-0 mt-3">
<h3 class="display-6">#Title</h3>
</div>
}
<div class="card-body">
#if (DataDictionary?.Any() ?? false)
{
#foreach (var kv in DataDictionary) {
<div class="row mb-3">
#if (!(LabelAbove && kv.Value?.DisplayName == ""))
{
<div class=#((LabelAbove ? "col-12" : "col-4"))>
#(kv.Value?.DisplayName ?? kv.Key)#if (kv.Value.IsRequired) { <span style="required-value">*</span> }
</div>
}
<div class=#((LabelAbove ? "col-12" : "col-8"))>
#if (kv.Value?.Template != null)
{
#kv.Value?.Template
} else
{
//When there is no template then it's just text - rendering in edit mode will require a text box
if (Editable && EditMode && kv.Value.IsEditable)
{
<SfTextBox Value=#kv.Value.Value ValueChange="#((__v) => updateDictValue(__v, kv.Key))" /><br />
<ValidationMessage For="() => kv.Value.Value" />
} else
{
#kv.Value?.Value
}
}
</div>
</div>
}
}
</div>
</div>
#code {
[Parameter]
public string Title { get; set; } = "";
[Parameter]
public IDictionary<string, clientmodels.FieldDisplayData> DataDictionary { get; set; } = null;
[Parameter]
public bool LabelAbove { get; set; } = false;
[Parameter] public bool Editable { get; set; } = true;
[CascadingParameter(Name = "EditMode")] public bool EditMode { get; set; }
[Parameter]
public EventCallback OnChanged { get; set; }
private async Task updateDictValue(ChangedEventArgs e, string key)
{
if (DataDictionary.ContainsKey(key))
{
DataDictionary[key].Value = e.Value;
await OnChanged.InvokeAsync();
}
}
}
PanelLayoutValidator.cs
namespace CustomStyle.Client.Code
{
public class _PanelLayoutValidationState
{
public string FullPath { get; set; }
}
public class _PanelLayoutFieldCssClassProvider : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier)
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
return isValid ? "e-success" : "e-error";
}
}
public class _PanelLayoutValidator : AbstractValidator<PanelLayoutData>
{
public _PanelLayoutValidator()
{
RuleForEach(l => l.panelData)
.ChildRules(l => {
l.RuleForEach(s => s.Panels)
.ChildRules(s => {
//We apply rules for [Required] [MinLength] [MaxLength] [CreditCard] [EmailAddress] [Range] [RegularExpression]
//Configure property names
var ddeach = s.RuleForEach(p => p.DisplayDictionary);
ddeach.ChildRules(kvconfig =>
{
kvconfig.RuleFor(kv => kv.Value.Value).Configure(cfg =>
{
cfg.MessageBuilder = context =>
{
context.MessageFormatter.AppendPropertyName(context.PropertyName);
return context.GetDefaultMessage();
};
});
});
//Non parametric validations
ddeach.ChildRules(kvconfig =>
{
//IsRequired
kvconfig.RuleFor(kv => kv.Value.Value)
.NotEmpty().When(x => x.Value != null && x.Value.IsRequired).WithMessage("{ParsedPropertyName} cannot be empty");
//CreditCard
kvconfig.RuleFor(kv => kv.Value.Value)
.CreditCard().When(x => x.Value != null && x.Value.IsCreditCard).WithMessage("{ParsedPropertyName} should be a Credit or Debit card number");
//EmailAddress
kvconfig.RuleFor(kv => kv.Value.Value)
.EmailAddress().When(x => x.Value != null && x.Value.IsCreditCard).WithMessage("{ParsedPropertyName} should be an Email Address");
});
//Parametric validations
ddeach.ChildRules(kvconfig =>
{
//MinLength
kvconfig.RuleFor(kv => new { Value = kv.Value.Value, Config = kv.Value })
.Must(vl => vl.Value.Length >= vl.Config.MinLength)
.When(x => x.Value != null && x.Value.MinLength != null)
.WithMessage(x => $"{{ParsedPropertyNameVV}} must have at least {x.Value.MinLength} characters");
//MaxLength
kvconfig.RuleFor(kv => new { Value = kv.Value.Value, Config = kv.Value })
.Must(vl => vl.Value.Length <= vl.Config.MaxLength)
.When(x => x.Value != null && x.Value.MaxLength != null)
.WithMessage(x => $"{{ParsedPropertyNameVV}} must have at most {x.Value.MaxLength} characters");
//Range
kvconfig.RuleFor(kv => new { Value = int.Parse(kv.Value.Value), Config = kv.Value })
.Must(vl => vl.Value >= vl.Config.Range[0] && vl.Value <= vl.Config.Range[1])
.When(x => x.Value != null && x.Value.Range != null)
.WithMessage(x => $"{{ParsedPropertyNameVV}} must be between {x.Value.Range[0]} and {x.Value.Range[1]}");
//Regex
kvconfig.RuleFor(kv => new { Value = kv.Value.Value, Config = kv.Value })
.Must(vl => System.Text.RegularExpressions.Regex.IsMatch(vl.Value, vl.Config.RegularExpression))
.When(x => x.Value != null && x.Value.RegularExpression != null)
.WithMessage(x => $"{{ParsedPropertyNameVV}} does not match the expected pattern");
});
//ToDo: Add rules for RefPoco routes based on object data annotation attributes
});
});
}
}
public class PanelLayoutValidator : ComponentBase
{
private readonly static char[] separators = new[] { '.', '[' };
private _PanelLayoutValidator validator;
[CascadingParameter] private EditContext EditContext { get; set; }
protected override void OnInitialized()
{
validator = new _PanelLayoutValidator();
var messages = new ValidationMessageStore(EditContext);
// Revalidate when any field changes, or if the entire form requests validation
// (e.g., on submit)
EditContext.OnFieldChanged += (sender, eventArgs)
=> ValidateModel((EditContext)sender, messages);
EditContext.OnValidationRequested += (sender, eventArgs)
=> ValidateModel((EditContext)sender, messages);
}
private string GetParsedPropertyName(EditContext context, FieldIdentifier id, string PropertyName)
{
//process the property path to calculate the property description
//If we're using the expected format for a dictionary field, we can read the display name and the key
var model = context.Model as PanelLayoutData;
var match = System.Text.RegularExpressions.Regex.Match(PropertyName, #"^panelData\[(?<section_index>[^\]]+)\].Panels\[(?<panel_index>[^\]]+)\].DisplayDictionary\[(?<field_key>[^\]]+)\].(?<target>Value|RefPoco)");
if (match.Success)
{
var section_index = int.Parse(match.Groups["section_index"].Value);
var section_name = model.panelData[section_index].Title;
var panel_index = int.Parse(match.Groups["panel_index"].Value);
var panel_name = model.panelData[section_index].Panels[panel_index].Title;
var property_name = "";
if (match.Groups["target"].Value == "Value")
{
var field_key_index = int.Parse(match.Groups["field_key"].Value);
var dict = model.panelData[section_index].Panels[panel_index].DisplayDictionary;
var field_key = dict.Keys.Skip(field_key_index).First();
property_name = dict[field_key].DisplayName ?? field_key;
} else
{
//TODO: Expand this to grab the property and look for DisplayName attributes
property_name = id.FieldName;
}
if (!string.IsNullOrEmpty(section_name)) {
section_name += "/";
}
if (!string.IsNullOrEmpty(panel_name))
{
panel_name += "/";
}
return $"{section_name}{panel_name}{property_name}";
} else {
//we have no section info so just pick up the property name
//TODO: Expand this to grab the property and look for DisplayName attributes
return id.FieldName;
}
}
private void ValidateModel(EditContext editContext, ValidationMessageStore messages)
{
var validationResult = validator.Validate((PanelLayoutData)editContext.Model);
messages.Clear();
foreach (var error in validationResult.Errors)
{
FieldIdentifier fieldIdentifier = default(FieldIdentifier);
var msg = error.ErrorMessage;
if (msg.Contains("{ParsedPropertyName}")) {
fieldIdentifier = ToFieldIdentifier(editContext, error.PropertyName);
msg = msg.Replace("{ParsedPropertyName}", GetParsedPropertyName(editContext, fieldIdentifier, error.PropertyName));
}
if (msg.Contains("{ParsedPropertyNameVV}"))
{
fieldIdentifier = ToFieldIdentifier(editContext, $"{error.PropertyName}.Value");
msg = msg.Replace("{ParsedPropertyNameVV}", GetParsedPropertyName(editContext, fieldIdentifier, $"{error.PropertyName}.Value"));
}
messages.Add(fieldIdentifier, msg);
}
editContext.NotifyValidationStateChanged();
}
private static FieldIdentifier ToFieldIdentifier(EditContext editContext, string propertyPath)
{
// This method parses property paths like 'SomeProp.MyCollection[123].ChildProp'
// and returns a FieldIdentifier which is an (instance, propName) pair. For example,
// it would return the pair (SomeProp.MyCollection[123], "ChildProp"). It traverses
// as far into the propertyPath as it can go until it finds any null instance.
var obj = editContext.Model;
while (true)
{
var nextTokenEnd = propertyPath.IndexOfAny(separators);
if (nextTokenEnd < 0)
{
return new FieldIdentifier(obj, propertyPath);
}
var nextToken = propertyPath.Substring(0, nextTokenEnd);
propertyPath = propertyPath.Substring(nextTokenEnd + 1);
object newObj;
if (nextToken.EndsWith("]"))
{
nextToken = nextToken.Substring(0, nextToken.Length - 1);
var tobj = obj.GetType();
if (obj is IDictionary)
{
//fluent validation indicates index in dictionary as an integer - dictionaries don't index like that
//grab the key at the given index
var kprop = tobj.GetProperty("Keys");
var keys = (ICollection)kprop.GetValue(obj);
object key = keys.Cast<object>().Skip(int.Parse(nextToken)).First();
var prop = tobj.GetProperty("Item");
newObj = prop.GetValue(obj, new object[] { key });
}
else
{
// It's an indexer
// This code assumes C# conventions (one indexer named Item with one param)
var prop = tobj.GetProperty("Item");
var indexerType = prop.GetIndexParameters()[0].ParameterType;
var indexerValue = Convert.ChangeType(nextToken, indexerType);
newObj = prop.GetValue(obj, new object[] { indexerValue });
}
}
else
{
// It's a regular property
var prop = obj.GetType().GetProperty(nextToken);
if (prop == null)
{
throw new InvalidOperationException($"Could not find property named {nextToken} on object of type {obj.GetType().FullName}.");
}
newObj = prop.GetValue(obj);
}
if (newObj == null)
{
// This is as far as we can go
return new FieldIdentifier(obj, nextToken);
}
obj = newObj;
}
}
}
}
PanelModels.cs
namespace CustomStyle.Client.Models
{
public class PanelData
{
public string Title { get; set; }
public Dictionary<string, FieldDisplayData> DisplayDictionary { get; set; } = new();
public bool IsFullWidth { get; set; } = false;
public bool IsSpacer { get; set; } = false;
public bool LabelAbove { get; set; } = false;
}
public class PanelSectionData
{
[Required]
public string Title { get; set; }
public List<PanelData> Panels { get; set; } = new();
}
public class PanelLayoutData : PageLayoutData
{
public string idPrefix { get; set; }
public List<PanelSectionData> panelData { get; set; } = new();
}
public class FieldDisplayData
{
public FieldDisplayData()
{
}
public FieldDisplayData(string value, bool isRequired = false)
{
DisplayName = null;
Value = value;
Template = null;
IsRequired = isRequired;
}
public FieldDisplayData(RenderFragment template, bool isRequired = false)
{
DisplayName = null;
Value = null;
Template = template;
IsRequired = isRequired;
}
public FieldDisplayData(string displayName, string value, bool isRequired = false)
{
DisplayName = displayName;
Value = value;
Template = null;
IsRequired = isRequired;
}
public FieldDisplayData(string displayName, RenderFragment template, bool isRequired = false)
{
DisplayName = displayName;
Value = null;
Template = template;
IsRequired = isRequired;
}
public string DisplayName { get; set; } = null;
public string Value { get; set; } = null;
public RenderFragment Template { get; set; } = null;
//If the Template references a child of the overall panel data tree,
//add a reference here to allow the validator to see it
public object RefPoco { get; set; } = null;
public bool IsEditable { get; set; } = true;
public bool IsRequired { get; set; } = false;
public int? MinLength { get; set; } = null;
public int? MaxLength { get; set; } = null;
}
}
Hopefully somebody might just know what I messed up.
Thanks.
This is how things work in the standard Blazor Input Controls, which is probably similar to the SynFusion controls (but as they are proprietary the jury is out).
The input control builds a FieldIdentifier from the ValueExpression that either you provide manually or the Razor compiler builds for you from a #bind-Value definition. When a component renders, it uses this FieldIdentifier to check for validation messages in the Validation Store and then applies the neccessary Css settings through a FieldCssClassProvider.
So either:
Your input components aren't re-rendering when the validation state changes in the form, or
The FieldIdentifier constructed by the input control doesn't match the one used to identify the validation message in the Validation Store, or
The Syncfusion controls operate to a different set of rules.
As I don't use Syncfusion and you're question is a bit of a wall of code without context I can only offer pointers as to where the problem might be. Hopefully someone with Syncfusion knowledge will provide a more solid answer. Good luck.
I have a list of raw data, where always 8 data values are available for one timestamp Date_Time. I would like to use linq to sort the values of Data_Value column by the Data_Type into one row for each Date_Time.
Rawdata table
I have the following class and would like to return it as a list of that class.
public class MeasurementData
{
public MeasurementData();
public int Test_ID { get; set; }
public int Channel { get; set; }
public string Date_Time { get; set; }
public double Current { get; set; }
public double Voltage { get; set; }
public double Charge_Capacity { get; set; }
public double Discharge_Capacity { get; set; }
}
Here is a reduced form of the code, where i just try to extract four values.
public static List<DataStructure.MeasurementData> RawResult(List<DataStructure.MeasurementRawTableSQL> rawData, int _Test_ID)
{
if (rawData != null)
{
var result = rawData.GroupBy(x => x.Date_Time)
.Select(gr =>
{
var _Date_Time = TicksToDate(gr.FirstOrDefault().Date_Time);
var _Channel = gr.FirstOrDefault().Channel;
var _Voltage = gr.Where(x => x.Data_Type == 21).FirstOrDefault().Data_Value;
var _Current = gr.Where(x => x.Data_Type == 22).FirstOrDefault().Data_Value;
var _Charge_Capacity = gr.Where(x => x.Data_Type == 23).FirstOrDefault().Data_Value;
var _Discharge_Capacity = gr.Where(x => x.Data_Type == 24).FirstOrDefault().Data_Value;
return new DataStructure.MeasurementData
{
Test_ID = _Test_ID,
Channel = _Channel,
Date_Time = _Date_Time,
Current = _Current,
Voltage = _Voltage,
Charge_Capacity = _Charge_Capacity,
Discharge_Capacity = _Discharge_Capacity
};
}
).ToList();
return result;
}
else return null;
}
This is partially working, for the case 21 and 22 it gives me proper values, whereas I get an error "Object reference not set to an instance of an object" for the case 23 and 24. On the other hand the database has this rows for every single datapoint and is never null. If I only select First() instead of FirstOrDefault() I get an "sequence contains no elements".
I am really stuck right now and would really appriciate your help.
Below is the cleanest way of solving issue. I cannot explain why you are not getting a Data_Type for each time value, but code below should get around the exceptions. I used decimal? as the type. The exception is caused when the WHERE returns null you cannot get the property Data_Value. So you have to test for null like code below :
class Program
{
static void Main(string[] args)
{
}
//public DataStructures DataStructure = new DataStructures();
public static List<DataStructure.MeasurementData> RawResult(List<DataStructure.MeasurementRawTableSQL> rawData, int _Test_ID)
{
if (rawData != null)
{
double? _Voltage = null;
double? _Current = null;
double? _Charge_Capacity = null;
double? _Discharge_Capacity = null;
var result = rawData.GroupBy(x => x.Date_Time)
.Select(gr =>
{
var _Date_Time = gr.Key;
var _Channel = gr.FirstOrDefault().Channel;
var _Voltage_Row = gr.Where(x => x.Data_Type == 21).FirstOrDefault();
if(_Voltage_Row != null) _Voltage = _Voltage_Row.Data_Value;
var _Current_Row = gr.Where(x => x.Data_Type == 22).FirstOrDefault();
if(_Current_Row != null) _Current = _Current_Row.Data_Value;
var _Charge_Capacity_Row = gr.Where(x => x.Data_Type == 23).FirstOrDefault();
if (_Charge_Capacity_Row != null) _Charge_Capacity = _Charge_Capacity_Row.Data_Value;
var _Discharge_Capacity_Row = gr.Where(x => x.Data_Type == 24).FirstOrDefault();
if (_Discharge_Capacity_Row != null) _Discharge_Capacity = _Discharge_Capacity_Row.Data_Value;
return new DataStructure.MeasurementData
{
Test_ID = _Test_ID,
Channel = _Channel,
Date_Time = _Date_Time,
Current = _Current,
Voltage = _Voltage,
Charge_Capacity = _Charge_Capacity,
Discharge_Capacity = _Discharge_Capacity
};
}
).ToList();
return result;
}
else return null;
}
}
public class DataStructure
{
public class MeasurementData
{
public int? Test_ID { get; set; }
public int? Channel { get; set; }
public DateTime Date_Time { get; set; }
public double? Current { get; set; }
public double? Voltage { get; set; }
public double? Charge_Capacity { get; set; }
public double? Discharge_Capacity { get; set; }
}
public class MeasurementRawTableSQL
{
public DateTime Date_Time { get; set; }
public int Channel { get; set; }
public int Data_Type { get; set; }
public double Data_Value { get; set; }
}
}
I can not update my database using EF (UpdateSpecified() method is not working). My Add() method works fine.
Edit: I observed the SaveChanges Method it always return 1 ,it let me confused because I updated 3 tables.
I discovered the RepositoryFactory's IQueryable code, it had changed:
My code:
public class GoodsModel
{
private IRepositoryFactory _repositoryFactory;
private IServiceFactory _serviceFactory;
private IGoodsService _goodsService;
private IGoodsTypeService _goodsTypeService;
private IUsersService _userService;
private IOrderService _orderService;
private IOrdersRepository orderRepo;
private IUsersRepository userRepo;
private IGoodsRepository goodsRepo;
public GoodsModel()
{
_repositoryFactory = new RepositoryFactory();
_repositoryFactory.OpenSession();
_serviceFactory = new ServiceFactory(_repositoryFactory);
_goodsService = _serviceFactory.CreateGoodsService();
_goodsTypeService = _serviceFactory.CreateGoodsTypeService();
_userService = _serviceFactory.CreateUsersService();
_orderService = _serviceFactory.CreateOrderService();
userRepo = _repositoryFactory.CreateUsersRepository();
orderRepo = _repositoryFactory.CreateOrdersRepository();
goodsRepo = _repositoryFactory.CreateGoodsRepository();
orderRepo = _repositoryFactory.CreateOrdersRepository();
}
public bool BuyProduct(BuyProductDto model)
{
string name = HttpContext.Current.User.Identity.Name;
// _repositoryFactory.OpenSession();
using (_repositoryFactory)
{
var user = _userService.Filter(x => x.UserName == name).FirstOrDefault();
var good = _goodsService.Filter(x => x.GoodNumber == model.GoodNumber).FirstOrDefault();
//var userRepo = _repositoryFactory.CreateUsersRepository();
//var goodRepo = _repositoryFactory.CreateGoodsRepository();
Users u = new Users();
Goods g = new Goods();
try
{
//substract when buy product.
int remainScore = user.UserScore - (int)good.GoodScore;
if (remainScore < 0)
{
return false;
}
u.UserId = user.UserId;
u.UserScore = remainScore;
userRepo.Attach(u);
userRepo.UpdateSpecified(u);
g.Id = good.Id;
//same as above syntax
g.GoodsQuantity = good.GoodsQuantity - 1;
goodsRepo.Attach(g);
goodsRepo.UpdateSpecified(g);
//orderRepo = _repositoryFactory.CreateOrdersRepository();
orderRepo.Add(new Orders
{
GoodId = good.Id,
InsertTime = DateTime.Now,
Status = 0,
UserId = user.UserId,
OrderNo = DateTime.Now.ToString("yyyyMMddss"),
UpdateTime = DateTime.Now
});
_repositoryFactory.Commit();
}
catch (Exception ex)
{
_repositoryFactory.RollBack();
return false;
}
}
return true;
}
}
The code of framework is here:
public void UpdateSpecified(T entity)
{
var type = entity.GetType();
if (type.IsPrimitive || type == typeof(string))
{
var props = type.GetProperties();
foreach (var prop in props)
{
string propValue = prop.GetValue(entity, null) != null ? prop.GetValue(entity, null).ToString() : string.Empty;
if (!string.IsNullOrEmpty(propValue))
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = true;
}
else
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = false;
}
}
}
}
public void Attach(T entity)
{
DataContext.Set<T>().Attach(entity);
}
}
The entity code like this, it contains the navigation attribute:
public class Users
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public DateTime InsertTime { get; set; }
public ICollection<UsersUserGroup> UsersUserGroups { get; set; }
public int UserScore { get; set; }
}
oaky.... I have solved it ,it is fool question ,I admit; Correct Code:
public void UpdateSpecified(T entity)
{
var props = entity.GetType().GetProperties();
foreach (var prop in props)
{
if (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(string))
{
string propValue = prop.GetValue(entity, null) != null ? prop.GetValue(entity, null).ToString() : string.Empty;
if (!string.IsNullOrEmpty(propValue))
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = true;
}
else
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = false;
}
}
}
}
I have a weird problem. I've made a couple of working wcf. I've updated one of them and it seems all ok.
But, if I call a method with a get within the browser, I get ERR_CONNECTION_RESET. Debbuging it, I've found that the code is looping, calling itself twice or three times, when the call is only one.
The code is really similar to other, but there is something I can't understand.
Why does it loop?
This is the code of my method:
public List<WrapImpianti> GetImpianto(string codret, string storeID, string hashedString, string outputType)
{
Utility.Logger("GetImpianto: start " + outputType);
var md5 = new md5Manager();
string key = "";
IS_RETISTI retista = getRetista(codret);
if (retista != null)
key = retista.CHIAVEDES;
List<WrapImpianti> r = new List<WrapImpianti>();
//return r;
if (md5.checkHashedInput(codret + storeID, hashedString, key))
{
try
{
using (var db = new AccessData.Entities())
{
var t = db.IS_IMPIANTI.Where(x => x.STOREID == storeID && x.IDRET == retista.ID).ToList();
r = t.Select(x => new WrapImpianti(x, key)).ToList();
}
return r;
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message, HttpStatusCode.Conflict);
}
}
else
throw new WebFaultException<string>("Hash non corretto", HttpStatusCode.BadRequest);
}
If I comment the row "r = t.Select(x => new WrapImpianti(x, key)).ToList();", it works... or, at least, it returns an empty object (because I don't populate it) without looping.
Why? It only calls the init method of my class WrapImpianti...
this is the class
[DataContract]
public class WrapImpianti
{
public WrapImpianti(){}
private IS_IMPIANTI model;
private string key;
public WrapImpianti(IS_IMPIANTI model, string key)
{
this.model = model;
this.key = key;
}
private string p_STOREID { get; set; }
[DataMember]
public string STOREID
{
get
{
if (model == null)
return p_STOREID.ToSafeString();
else
return this.model.STOREID.ToSafeString();
}
set { p_STOREID = value; }
}
private string p_CODICE { get; set; }
[DataMember]
public string CODICE
{
get
{
if (model == null)
return p_CODICE.ToSafeString();
else
return this.model.CODICE.ToSafeString();
}
set { p_CODICE = value; }
}
private string p_CODRETE { get; set; }
[DataMember]
public string CODRETE
{
get
{
if (model == null)
return p_CODRETE.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
var pp = db.IS_RETISTI.FirstOrDefault(x => x.ID == this.model.IDRET);
if (pp != null)
return pp.CODICE;
else
return "";
}
}
}
set { p_CODRETE = value; }
}
private string p_INDIRIZZO { get; set; }
[DataMember]
public string INDIRIZZO
{
get
{
if (model == null)
return p_INDIRIZZO.ToSafeString();
else
return this.model.INDIRIZZO.ToSafeString();
}
set { p_INDIRIZZO = value; }
}
private string p_CITTA { get; set; }
[DataMember]
public string CITTA
{
get
{
if (model == null)
return p_CITTA.ToSafeString();
else
return this.model.CITTA.ToSafeString();
}
set { p_CITTA = value; }
}
private string p_STATO { get; set; }
[DataMember]
public string STATO
{
get
{
if (model == null)
return p_STATO.ToSafeString();
else
return this.model.STATO.ToSafeString();
}
set { p_STATO = value; }
}
private string p_COD_ESERCENTE { get; set; }
[DataMember]
public string COD_ESERCENTE
{
get
{
if (model == null)
return p_COD_ESERCENTE.ToSafeString();
else
return this.model.COD_ESERCENTE.ToSafeString();
}
set { p_COD_ESERCENTE = value; }
}
private string p_TERMINAL_ID { get; set; }
[DataMember]
public string TERMINAL_ID
{
get
{
if (model == null)
return p_TERMINAL_ID.ToSafeString();
else
return this.model.TERMINAL_ID.ToSafeString();
}
set { p_TERMINAL_ID = value; }
}
private string p_COL { get; set; }
[DataMember]
public string COL
{
get
{
if (model == null)
return p_COL.ToSafeString();
else
return this.model.COL.ToSafeString();
}
set { p_COL = value; }
}
private string p_NOME { get; set; }
[DataMember]
public string NOME
{
get
{
if (model == null)
return p_NOME.ToSafeString();
else
return this.model.NOME.ToSafeString();
}
set { p_NOME = value; }
}
private string p_CAP { get; set; }
[DataMember]
public string CAP
{
get
{
if (model == null)
return p_CAP.ToSafeString();
else
return this.model.CAP.ToSafeString();
}
set { p_CAP = value; }
}
private string p_TELUFF { get; set; }
[DataMember]
public string TELUFF
{
get
{
if (model == null)
return p_TELUFF.ToSafeString();
else
return this.model.TELUFF.ToSafeString();
}
set { p_TELUFF = value; }
}
private string p_TELCELL { get; set; }
[DataMember]
public string TELCELL
{
get
{
if (model == null)
return p_TELCELL.ToSafeString();
else
return this.model.TELCELL.ToSafeString();
}
set { p_TELCELL = value; }
}
private string p_NOTE { get; set; }
[DataMember]
public string NOTE
{
get
{
if (model == null)
return p_NOTE.ToSafeString();
else
return this.model.NOTE.ToSafeString();
}
set { p_NOTE = value; }
}
private string p_PIVA { get; set; }
[DataMember]
public string PIVA
{
get
{
if (model == null)
return p_PIVA.ToSafeString();
else
return this.model.PIVA.ToSafeString();
}
set { p_PIVA = value; }
}
private string p_CODICEFISCALE { get; set; }
[DataMember]
public string CODICEFISCALE
{
get
{
if (model == null)
return p_CODICEFISCALE.ToSafeString();
else
return this.model.CODICEFISCALE.ToSafeString();
}
set { p_CODICEFISCALE = value; }
}
private string p_GESTORE { get; set; }
[DataMember]
public string GESTORE
{
get
{
if (model == null)
return p_GESTORE.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
return db.IS_GESTORI.FirstOrDefault(x => x.ID == this.model.IDGES).CODICE.ToSafeString();
}
}
}
set { p_GESTORE = value; }
}
private string p_PROPRIETARIO { get; set; }
[DataMember]
public string PROPRIETARIO
{
get
{
if (model == null)
return p_PROPRIETARIO.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
return db.IS_PROPRIETARI.FirstOrDefault(x => x.ID == this.model.IDPRP).CODICE.ToSafeString();
}
}
}
set { p_PROPRIETARIO = value; }
}
}
Why, if I call my class init method, does it start looping? And then, why does it return ERR_CONNECTION_RESET?
Ok, this is interesting. The solutions was here:
public string PROPRIETARIO
{
get
{
if (model == null)
return p_PROPRIETARIO.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
return db.IS_PROPRIETARI.FirstOrDefault(x => x.ID == this.model.IDPRP).CODICE.ToSafeString();
}
}
}
set { p_PROPRIETARIO = value; }
in my customclass.. sometimes, the IDPRP is null and raises this error.
It is very interisting: I was expecting this type of error was more visible to the developer, instead of loop itself and get an anonymous error.
Hope it can be useful for someone. ;)
How to mock this class in nUnit Tests?
public class OpenDataQuery: PagedQuery, IOpenDataQuery
{
private static Dictionary<string, SortItem> m_sortModes;
protected override Dictionary<string, SortItem> SortModes
{
get
{
if (m_sortModes == null)
{
m_sortModes = new Dictionary<string, SortItem>();
AddSortMode(m_sortModes, new SortItem(ObjectExtensions.GetNameFromExpression<OpenDataCategoriesModel, string>(m => m.Name), "Наименование ↑", true) { IsDefault = true });
AddSortMode(m_sortModes, new SortItem(ObjectExtensions.GetNameFromExpression<OpenDataCategoriesModel, string>(m => m.Name), "Наименование ↓"));
}
return m_sortModes;
}
}
public IEnumerable<OpenDataCategoriesModel> OpenDataCategories { get; set; }
public string OpenDataTags { get; set; }
}
and
public abstract class PagedQuery : IPagedQuery
{
private const int DEFAULT_PAGE = 1;
private const int DEFAULT_COUNT = 5;
private int? m_page;
private int? m_count;
private int? m_total;
private string m_sort;
public int? Page
{
get
{
if (m_page == null || m_page <= 0)
{
return DEFAULT_PAGE;
}
return m_page;
}
set { m_page = value; }
}
public int? Count
{
get
{
if (m_count == null || m_count <= 0)
{
return DEFAULT_COUNT;
}
return m_count;
}
set { m_count = value; }
}
public int? Total
{
get
{
if (m_total == null || m_total <= 0)
{
return 0;
}
return m_total;
}
set { m_total = value; }
}
public string SearchQuery { get; set; }
protected virtual Dictionary<string, SortItem> SortModes
{
get { return null; }
}
public string Sort
{
get
{
var sortMode = GetSortMode(m_sort);
if (sortMode == null)
{
var defaultSort = (from i in SortModes where i.Value.IsDefault select i).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(defaultSort.Key))
{
return defaultSort.Key;
}
return (from i in SortModes select i.Key).First();
}
return m_sort;
}
set
{
m_sort = value;
}
}
protected void AddSortMode(Dictionary<string, SortItem> sortModes, SortItem sortItem)
{
sortModes.Add(
String.Format(
"{0}{1}",
sortItem.FieldName.ToLower(),
sortItem.Asc ? "asc" : "desc"
),
sortItem
);
}
private SortItem GetSortMode(string sort)
{
if (SortModes == null || string.IsNullOrWhiteSpace(sort) ||
!SortModes.ContainsKey(sort.ToLower()))
{
return null;
}
return SortModes[sort.ToLower()];
}
public IOrderBy GetOrderBy()
{
var item = GetCurrentSortItem();
if (item == null)
{
return null;
}
if (item.Asc)
{
return new OrderBy(item.FieldName);
}
return new OrderByDesc(item.FieldName);
}
public SortItem GetCurrentSortItem()
{
return GetSortMode(Sort);
}
public Dictionary<string, SortItem> GetSortItems()
{
return SortModes;
}
}
and
public interface IOpenDataQuery : IPagedQuery
{
string OpenDataTags { get; set; }
}
I have some service method, that used openDataQuery class in parameters and in unit test i am trying mock this class, but this doesn't work:
public partial class OpenDataQueryRepository : Mock<OpenDataQuery>
{
public OpenDataQueryRepository(MockBehavior mockBehavior = MockBehavior.Strict)
: base(mockBehavior)
{
var opendataQuery = new Mock<IOpenDataQuery>();
var pagedQuery = opendataQuery.As<IPagedQuery>();
this.Setup(p=>p.GetOpenDataCategoriesMain(pagedQuery.Object,outttl)).Returns(OpenDataCategories);
}
}
I know that i should use Moq.Protected() for protected methods, but i don't know how use it correctly in this case. Please help me.
UPDATE:
I am testing this controller:
public class ODOpenDataController : ODBaseController
{
private readonly IOpenDataProvider m_openDataProvider;
public ODOpenDataController(IOpenDataProvider openDataProvider)
{
m_openDataProvider = openDataProvider;
}
public ActionResult Index(OpenDataQuery query)
{
int total;
query.OpenDataCategories = m_openDataProvider.GetOpenDataCategoriesMain(query, out total)
query.Total = total;
return View(query);
}
}
Test:
[Test]
public void Index_Test()
{
var opendataController = new ODOpenDataController(new OpenDataRepository().Object);
var result = opendataController.Index(new OpenDataQuery()) as ViewResult;
var model = result.Model as OpenDataQuery;
Assert.IsTrue(model.OpenDataCategories.Count() == 1);
}