Background:
I have ASP.NET Core app. I'm trying to write server-side validation errors to the log.
If ASP.NET Core model validation detects any error in any field in the webform, then ModelState.IsValid is false.
Every field in the webform is listed in ModelState.Values
If a "Value" has one or more errors, then Value.Errors.Count > 0
I want to log a) the webform field name (the Value's "key") and b) each error message for that field.
I'd like to include all this information in a single line (i.e. a single C# "string").
Current code:
// LOG:
// 2022-10-24 09:37:29.0909|Error|ServiceMenu.Q255: The value '' is invalid.
// 2022-10-24 09:37:35.4096|Error|ServiceMenu.Q265: The value '' is invalid.
if (!ModelState.IsValid)
{
foreach (var k in ModelState.Keys)
{
var v = ModelState[k];
if (v.Errors.Count > 0)
{
string s = k + ": ";
for (int i=0; i < v.Errors.Count - 1; i++)
s += v.Errors[i].ErrorMessage + ", ";
s += v.Errors[v.Errors.Count - 1].ErrorMessage;
_logger.LogError(s);
}
}
return Page();
}
Sample output (two required fields, Q255 and 265, were blank):
2022-10-24 09:37:29.0909|Error|ServiceMenu.Q255: The value '' is invalid.
2022-10-24 09:37:35.4096|Error|ServiceMenu.Q265: The value '' is invalid.
Q: Can I simplify this nested loop with LINQ?
Update:
Thank you, everybody.
Evk and T.S. are both absolutely correct: "Don't think that using LINQ is always good." I don't - and their point is very well taken :)
Oliver Weichhold gave me the syntax I was looking for :)
Here's the code I finally wound up with:
// EXAMPLE LOG:
// 2022-10-24 13:24:10.5242|Error|ServiceMenu.Q255: The value '' is invalid.
// 2022-10-24 13:24:24.9692|Error|ServiceMenu.Q265: The value '' is invalid.
if (!ModelState.IsValid)
{
foreach (var k in ModelState.Keys)
{
var v = ModelState[k];
if (v.Errors.Count > 0)
{
string s = string.Join(", ", ModelState[k].Errors
.Select(x => x.ErrorMessage));
_logger.LogError(k + ": " + s);
}
}
...
if (!ModelState.IsValid)
{
var logMessage = string.Join("\n", ModelState.Keys
.Where(x => ModelState[x].Errors?.Count > 0)
.Select(x => $"{x}: " + string.Join(", ", ModelState[x].Errors.Select(y => y.ErrorMessage))));
_logger.LogError(logMessage);
}
Related
I am using the following C# code to parse a csv file, after which the results will be exported to a SQL Server database.
What I am trying to do with the if statement is say "if the value in column 1 is 1, parse that row one way, if the value in column 1 is 2, parse that row another way..." what I then need to add, where I have the comment in the code below, is to say "otherwise, just skip that row of the csv file."
public List<Entity> ParseCsvFile(List<string> entries, string urlFile)
{
entries.RemoveAt(entries.Count - 1);
entries.RemoveAt(0);
List<Entity> entities = new List<Entity>();
foreach (string line in entries)
{
Entity CsvFile = new Entity();
string[] lineParts = line.Split(',');
if (lineParts[1] == "1")
{
CsvFile.Identifier = $"{lineParts[2]}";
CsvFile.SourceId = $"{lineParts[3]}";
CsvFile.Name = $"{lineParts[5]} {lineParts[6]} {lineParts[7]} {lineParts[8]} " +
$"{lineParts[9]} {lineParts[10]}";
entities.Add(CsvFile);
}
else if (lineParts[1] == "2")
{
CsvFile.Identifier = $"{lineParts[11]}";
CsvFile.SourceId = $"{lineParts[12]}";
CsvFile.Name = $"{lineParts[13]} {lineParts[14]} {lineParts[15]};
entities.Add(CsvFile);
}
//Need to put code here that says "otherwise, skip this line of the CSV file."
}
return entities;
}
Based on this comment, I infer that at least part of your problem is that it isn't that you are having trouble with the syntax of the if statements, but rather that the element you're looking for in the array simply doesn't exist (e.g. if the whole row is blank, or at least has no commas).
Assuming that's the case, then this approach would be more reliable (this will ignore lines that don't have a second field, as well as those where the field doesn't contain an integer value, in case that was yet another issue you might have run into at some point):
if (lineParts.Length < 2 || !int.TryParse(lineParts[1], out int recordType))
{
continue;
}
if (recordType == 1)
{
CsvFile.Identifier = $"{lineParts[2]}";
CsvFile.SourceId = $"{lineParts[3]}";
CsvFile.Name = $"{lineParts[5]} {lineParts[6]} {lineParts[7]} {lineParts[8]} " +
$"{lineParts[9]} {lineParts[10]}";
entities.Add(CsvFile);
}
else if (recordType == 2)
{
CsvFile.Identifier = $"{lineParts[11]}";
CsvFile.SourceId = $"{lineParts[12]}";
CsvFile.Name = $"{lineParts[13]} {lineParts[14]} {lineParts[15]};
entities.Add(CsvFile);
}
For what it's worth, an expression like $"{lineParts[2]}" where the lineParts is already a string[] is pointless and inefficient. And the string.Join() method is helpful if all you want to do is concatenate string values using a particular separator. So, your code could be simplified a bit:
if (lineParts.Length < 2 || !int.TryParse(lineParts[1], out int recordType))
{
continue;
}
if (recordType == 1)
{
CsvFile.Identifier = lineParts[2];
CsvFile.SourceId = lineParts[3];
CsvFile.Name = string.Join(" ", lineParts.Skip(5).Take(6));
entities.Add(CsvFile);
}
else if (recordType == 2)
{
CsvFile.Identifier = lineParts[11];
CsvFile.SourceId = lineParts[12];
CsvFile.Name = string.Join(" ", lineParts.Skip(13).Take(3));
entities.Add(CsvFile);
}
Finally, consider not trying to parse CSV with your own code. The logic you have implemented will work only for the simplest examples of CSV. If you have complete control over the source and can ensure that the file will never have to do things like quote commas or quotation mark characters, then it may work okay. But most CSV data comes from sources outside one's control and it's important to make sure you can handle all the variants found in CSV. See Parsing CSV files in C#, with header for good information on how to do that.
I am working on a data migration project - API to API.
The destination API is graphQL, we have a number of objects to push into the destination and the shapes vary so I am looking for some advice on how best to dynamically build mutations/queries specifically in c#.
Currently we are just using templates and using find/replace routines to inject values. While this approach does work as the shapes of the data vary this becomes evermore complex and inelegant.
I am looking for any advice/pointers from anyone who have may have had a similar scenario or knows of any libraries I should look at.
Update - 13/02/2018
I have since updated this monstrosity to cater for nested sub selections and GraphQl enums so if anyone is interested here it is in GitHub
Orignal answer
I've got the same requirement. Couldn't find anything out there so I've come up with this very inelegant solution. It works for my scenarios so I'll post it here for anyone else looking for a solution.
public static class GraphQlObjectParser
{
public static string Parse(string queryType, string queryName, string[] subSelection, object #object = null, string objectTypeName = null)
{
var query = queryType + "{" + queryName;
if (#object != null)
{
query += "(";
if (objectTypeName != null)
{
query += objectTypeName + ":" + "{";
}
var queryData = string.Empty;
foreach (var propertyInfo in #object.GetType().GetProperties())
{
var value = propertyInfo.GetValue(#object);
if (value != null)
{
var type = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
var valueQuotes = type == typeof(string) ? "\"" : string.Empty;
var queryPart = char.ToLowerInvariant(propertyInfo.Name[0]) + propertyInfo.Name.Substring(1) + ":" + valueQuotes + value + valueQuotes;
queryData += queryData.Length > 0 ? "," + queryPart : queryPart;
}
}
query += (objectTypeName != null ? queryData + "}" : queryData) + ")";
}
if (subSelection.Length > 0)
{
query += subSelection.Aggregate("{", (current, s) => current + (current.Length > 1 ? "," + s : s)) + "}";
}
query += "}";
return query;
}
}
This works for both queries and mutations. Examples of usage are:
var query = GraphQlObjectParser.Parse("query", "users", new[] { "id", "name" });
will give you
query{users{id,name}}
or
var query = GraphQlObjectParser.Parse("query", "user", new[] { "id", "name" }, new User { Id = 1 });
will give you
query{user(id:1){id,name}}
or
var query = GraphQlObjectParser.Parse("mutation", "user", new[] { "id", "name" }, new User { Id = 1, Name = "John" }, "data");
will give you
mutation{user(data:{id:1,name:"John"}){id,name}}
It'll work with enums which is why I needed this solution in the first place. You can pass in a sub selection without the object or the object without an object type name. I've tried to cover as many bases as possible although I've not yet catered for sub selection of a sub selection. I'll update here if/when I code this one.
Hope it helps someone.
There is a cool library that helps you with building your GraphQL queries in the fluent API style. https://charlesdevandiere.github.io/graphql-query-builder-dotnet/
Hi i have tried to get address1_latitude and address1_longitude from CRM using CRM SDK here is mu code
var querybyattribute11 = new QueryByAttribute("account");
querybyattribute11.ColumnSet = new ColumnSet("name", "address1_city", "statuscode", "address1_postalcode", "address1_latitude", "address1_longitude");
querybyattribute11.Attributes.AddRange("name");
querybyattribute11.Values.AddRange("ASSOCIATED COMBUSTION INC");
EntityCollection entities = service.RetrieveMultiple(querybyattribute11);
foreach (Entity item in entities.Entities)
{
// Console.WriteLine("Name: {0}. Id: {1}", role.Name, role.Id);
list += item.Attributes["name"].ToString() + " " + item.Attributes["address1_longitude"] .ToString() + "\n";
}
But I am not geting it item.Attributes["address1_longitude"]
error message is
'The given key was not present in the dictionary.'
It might be because it's null.
Try either one of these 2 options:
item["address1_longitude"] (shouldn't raise exception, it would return null if blank, otherwise the address longitude)
To check if the column exists:
item.Attributes.ContainsKey("address1_longitude")
I am currently developing a MS Dynamics CRM 2013 - Plugin.
When I try to assign a string-value to a key of a field of an entity it gives me the 'keynotfound'-exception.
This leaves me clueless, because I can verify the key is existing. The key I give is also written correctly, and the data types are compatible, too.
Here's some extra info:
I tried resolving the issue with a server reboot. Nothing.
Remote Debugging is not an option.
I swapped "retrieved.EntityCollection.Entities[i][forField]" with retrieved.EntityCollection.Entities[i]["new_name"] and everything was working fine (kind of pointing out the obvious here, but "new_name" is not the key I try to access).
The execution stops # "if (retrieved.EntityCollection.Entities[i][forField].ToString() != "" && !overwriteExisting)"
Have you got an idea to help me out?
public void GenerateNumberForEntityCollection(string target)
{
try
{
// variables for number generation
bool overwriteExisting = (bool)preImageEntity["new_overwriteexisting"];
int suffixstart = (int)preImageEntity["new_suffixstart"];
string forField= preImageEntity["new_forfield"].ToString();
string prefix = preImageEntity["new_prefix"].ToString();
string postfix = preImageEntity["new_postfix"].ToString();
string separator = preImageEntity["new_separator"].ToString();
// Build query to get all the entries
RetrieveMultipleResponse retrieved;
int PageNumber = 1;
string PagingCookie = string.Empty;
int PageSize = 5000;
string[] Columns = { forField };
QueryExpression query = new QueryExpression()
{
EntityName = target,
ColumnSet = new ColumnSet(Columns),
PageInfo = new PagingInfo()
{
PageNumber = 1,
Count = PageSize
}
};
do
{
if (PageNumber != 1)
{
query.PageInfo.PageNumber = PageNumber;
query.PageInfo.PagingCookie = PagingCookie;
}
RetrieveMultipleRequest retrieve = new RetrieveMultipleRequest();
retrieve.Query = query;
retrieved = (RetrieveMultipleResponse)service.Execute(retrieve);
// Now that all entities are retrieved, iterate through them to gen. the numbers
int i = 0;
foreach (Entity entity in retrieved.EntityCollection.Entities)
{
if (retrieved.EntityCollection.Entities[i][forField].ToString() != "" && !overwriteExisting)
{
//continue;
}
else
{
retrieved.EntityCollection.Entities[i][forField] = prefix + separator + suffixstart.ToString() + separator + postfix;
}
suffixstart++;
service.Update(retrieved.EntityCollection.Entities[i]);
i++;
}
if (retrieved.EntityCollection.MoreRecords)
{
PageNumber++;
PagingCookie = retrieved.EntityCollection.PagingCookie;
}
} while (retrieved.EntityCollection.MoreRecords);
}
catch (Exception e)
{
tracing.Trace("GenerateNumberForEntityCollection: Failed: {0}", e.ToString());
}
}
How did you verify that the key exists?
If the data in a field is null, the Entity instance will not contain that key, even if you specify it in the query's ColumnSet.
This will return you a boolean, indicating if the key exists in the Entity. You can do this control before attempting to read the attribute.
var attributeExists = retrieved.EntityCollection.Entities[i].Contains(forField)
The control below you've done will result in the exception you're getting if the field is null. Just make sure that the attribute exists before.
retrieved.EntityCollection.Entities[i][forField].ToString() != ""
Additionally, you'll get a null reference exception if no records were returned from the query. Make you do a null check on retrieved.EntityCollection.Entities.
When you are querying data in Dynamics CRM it is important to know that record fields having null values in the database are not included in the Attributes collection of the Entity instances being returned.
Getting a value from an Entity's Attribute with this construct:
var value = retrieved.EntityCollection.Entities[i][forField].ToString();
succeeds when attribute forField already has a value in the database, but fails when its current value is null.
Therefore the preferred method to get the attribute values from an entity is GetAttributeValue<T>, like this:
var value = retrieved.EntityCollection.Entities[i].getAttributeValue<string>(forField);
This method returns the value when the attribute exists in the attribute collection, otherwise it returns null.
If any of the fields among
(new_forfield,new_prefix,new_postfix,new_separator) has null value,
that column does not present in the retrieved object and you are trying to get the value of null column preImageEntity["new_forfield"] which will throw keynotfound'-exception ,
so change the code
string forField= preImageEntity["new_forfield"].ToString();
string prefix = preImageEntity["new_prefix"].ToString();
string postfix = preImageEntity["new_postfix"].ToString();
string separator = preImageEntity["new_separator"].ToString();
to
string forField = preImageEntity.Attributes.Contains("new_forfield")? preImageEntity["new_forfield"].ToString():"";
string prefix = preImageEntity.Attributes.Contains("new_forfield") ? preImageEntity["new_prefix"].ToString() : "";
string postfix = preImageEntity.Attributes.Contains("new_forfield") ? preImageEntity["new_postfix"].ToString() : "";
string separator = preImageEntity.Attributes.Contains("new_forfield") ? preImageEntity["new_separator"].ToString() : "";
this will check for field, if it exists than will parse the value to
string else will assign empty string.
For some reason, when I add the ternary if statement to this bit of code, a NullPointerException is thrown. I'm not sure quite why...any ideas? This is the method for jqGrid - returning the Json data.
var gridModel = from entity in vendorList.AsQueryable()
select new
{
VendorId = "<a href='/Admin/DetailsPlan/" + entity.VendorId + "'><img src='/Images/next_icon_sm.png' class='icon' alt='View Vendor' /></a>",
VendorNm = entity.VendorNm,
Phone = (entity.Phone.Length < 5) ? String.Format("{0:(###) ###-####}", Convert.ToInt64(entity.Phone)) : entity.Phone,
City = entity.City,
State = entity.LkState.StateAbbr
};
Can you not have a ternary if statement in that location?
var gridModel = from entity in vendorList.AsQueryable()
let unformattedPhone = entity.Phone??string.Empty
select new
{
VendorId = "<a href='/Admin/DetailsPlan/" + entity.VendorId + "'><img src='/Images/next_icon_sm.png' class='icon' alt='View Vendor' /></a>",
VendorNm = entity.VendorNm,
Phone = (unformattedPhone.Length < 5) ? String.Format("{0:(###) ###-####}", Convert.ToInt64(unformattedPhone)) : unformattedPhone,
City = entity.City,
State = entity.LkState.StateAbbr
};
This may solve your problem.
One question, is entity.Phone null? If so, that would be the cause.
Side note: I have to say, that is an odd way of storing a phone number..
UPDATE
The problem is with the "entity.Phone.Length" part. If Phone is null, then you can't access it's length property... hence the error. So you need to add a null test. Something like:
Phone = ((entity.Phone != null) && (entity.Phone.Length < 5)) ? String.Format("{0:(###) ###-####}", Convert.ToInt64(entity.Phone)) : entity.Phone
That way, if it is null you are just emitting a null value.