Using linq for validation/ throwing exception - c#

This may be the wrong way to do things(!) but i am just wondering ....
I am using
//check that all transform fields have corresponding database columns
foreach (string sDatabaseFieldName in l_sDatabaseFieldNames)
{
bool bFound = false;
foreach (SqlParameter sqlp in sqlcmdAll.Parameters)
{
if (sqlp.SourceColumn == sDatabaseFieldName) bFound = true;
}
if (!bFound)
{
throw new Exception(string.Format("Transform field {0} does not have a corresponding column in the target table.", sDatabaseFieldName));
}
}
where l_sDatabaseFieldNames is a List< string> and sqlcmdAll is an insert SqlCommand with validated parameter names.
I want to throw an exception if an item in l_sDatabaseFieldNames is not in any sqlcmdAll.Parameters.SourceColumn. In other words all column names contained in l_sDatabaseFieldNames should also have a matching parameter (compared using SourceColumn property).
I can also use
bool bFound = l_sDatabaseFieldNames.All(sDBFN => sqlcmdAll.Parameters.Cast<SqlParameter>().Any(param => sDBFN == param.SourceColumn));
but i only get a true/false result.
Can i use a combination of the two techniques and throw an exception from within the linq query if an item is in l_sDatabaseFieldNames, but not in any sqlcmdAll.Parameters.SourceColumn?
Thank you in advance,
James.

What exception would you expect it to throw? I feel this is better for you to take the result and throw the exception you want to throw:
if (!l_sDatabaseFieldNames.All(sDBFN => sqlcmdAll.Parameters.Cast<SqlParameter>().Any(param => sDBFN == param.SourceColumn)))
{
throw new YourCustomException("Your custom message");
}
Of course, if this is for debugging purposes only to verify a condition in testing and you don't need it to go into the actual release code you can use an assertion:
Debug.Assert(l_sDatabaseFieldNames.All(sDBFN => sqlcmdAll.Parameters.Cast<SqlParameter>().Any(param => sDBFN == param.SourceColumn)));
* UPDATE *
Based on your comment, you have a few choices, basically. We could bastardize a select clause but I don't like this as it feels kinda weird throwing in a projection. Sadly Linq doesn't already have a ForEach() that lets you perform an Action<T>, so you can either write your own ForEach() for IEnumerable<T>, or you can use ToList() to convert the sequence to a List<T> which does have a ForEach():
sqlcmdAll.Parameters.Cast<SqlParameter>().ToList().ForEach(p =>
{
if (!l_sDatabaseFieldNames.Contains(p.SourceColumn))
{
throw new Exception("Could not find " + p.SourceColumn);
}
});
If you don't mind writing your own extension method, you can add this to your library (comes in very handy) to give ForEach() to IEnumerable:
public static class EnumerableExtensions
{
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source,
Action<T> action)
{
if (source == null) throw new ArgumentNullException("source");
foreach (T element in source)
{
action(element);
}
return source;
}
}
Which would let you do this instead of using ToList():
sqlcmdAll.Parameters.Cast<SqlParameter>().ForEach(p =>
{
if (!l_sDatabaseFieldNames.Contains(p.SourceColumn))
{
throw new Exception("Could not find " + p.SourceColumn);
}
});

You could select the first non matching parameter
SqlParameter sqlp = sqlcmdAll.Parameters.Cast<SqlParameter>().Where(param => !l_sDatabaseFieldNames.Contains(param.SourceColumn)).FirstOrDefault();
if (sqlp != null)
throw new Exception(string.Format("Transform field {0} does not have a corresponding column in the target table.", sqlp.SourceColumn));

Related

Loop should be simplified by calling Select - sonar cloud error c#

I get SonarCloud error in this code:
foreach (var item in itemList)
{
if (string.IsNullOrEmpty(item.Name))
{
throw new BadRequestException("Item name is null or missing...");
}
if (someOtherList.Any(x => x.Name == item.Name))
{
throw new NotAcceptedException("Item name already exist in Db.");
}
}
My question is how to translate this code into LINQ using Select (as sonar suggest) when I have exceptions?
I found the solution in order for sonar to be happy. :) But honestly it makes sense.
So, sometimes you can get the this error when you want to iterate through whole object but you just need one property, like in this situation. Here I want to iterate only through the names, so the solution will be:
foreach (var itemName in itemList.Select(x => x.Name))
{
if (string.IsNullOrEmpty(itemName))
{
throw new BadRequestException("Item name is null or missing...");
}
if (someOtherList.Any(x => x.Name == itemName))
{
throw new NotAcceptedException("Item name already exist in Db.");
}
}

Generic method with dynamically selected parameter in predicate

I have many objects of diffrent type and i need to check few diffrent properties from each of them in the same way. I want to use this method inside object initializer like this:
collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType
{
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = MY_METHOD_HERE(group),
Name = MY_METHOD_HERE(group)
})
Strongly typed example for one of the properties:
private ItemType CheckItemTypeConsistency(IGrouping<string, Item> group)
{
if (group.Any(x => x.ItemType != group.First().ItemType))
{
throw new ArgumentException($"Item with number {group.Key} is inconsistent", nameof(Item.ItemType));
}
else
{
return group.First().ItemType;
}
}
But i also have other property in Item that needs to be checked in the same way with diffrent type, so i have similar method but .ItemType is changed everywhere to .Name and return type is string.
I also have diffrent object type that i need to use it for, so in another method Item is changed to Vehicle.
How to create generic method like that?
I tried something like this:
private TElement CheckConsistency<TKey, TElement>(IGrouping<TKey, TElement> group, (maybe something here?))
{
if (group.Any(x => x.(what here?) != group.First().(what here?)))
{
throw new ArgumentException($"Element with number {group.Key} is inconsistent");
}
else
{
return group.First();
}
}
I solved problem with returning value by returning whole item so i can just CheckConsistency().Property when invoking this method.
But i dont know what to do with (what here?).
I thought that maybe i can put something in (maybe something here?) that would be somehow used in place of (what here?)?
Any ideas? I'm not sure about reflection because this method could be called easily more than 1000 times depending on collection size and number of unique entries.
#Edit:
Lets say it's something like merging data from two files. For example 2 data sets of items where quantity is added together for items with the same identifier etc. but some properties should be the same like name and if they are diffrent then there's something wrong and i need to throw an error.
There are diffrent sets of data with completly diffrent properties like vehicles, but rules are similar, some fields are just added together etc, some must be identical.
Using an accessor function and genericizing the property type and object type, you have:
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Func<TClass, TProp> propFn) {
var firstPropValue = propFn(group.First());
if (group.Any(x => firstPropValue == null ? propFn(x) == null : !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent");
}
else {
return firstPropValue;
}
}
Which you can use like:
var ans = collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType {
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = CheckConsistency(group, x => x.ItemType),
Name = CheckConsistency(group, x => x.Name)
});
If it is important to include the proper argument name in the exception, you could pass it in, take in an Expression<Func<>> and pull out the name, then compile the argument to a lambda to use (may be slow), or use reflection instead of a lambda property accessor (also possibly slow).
To use Reflection, I recommend caching the compiled function so you don't constantly re-compile each time the method is called:
// [Expression] => [Func]
Dictionary<LambdaExpression, Delegate> propFnCache = new Dictionary<LambdaExpression, Delegate>();
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Expression<Func<TClass, TProp>> propExpr) {
Func<TClass,TProp> propFn;
if (propFnCache.TryGetValue(propExpr, out var propDel))
propFn = (Func<TClass, TProp>)propDel;
else {
propFn = propExpr.Compile();
propFnCache.Add(propExpr, propFn);
}
var firstPropValue = propFn(group.First());
if (group.Any(x => !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent", ((MemberExpression)propExpr.Body).Member.Name);
}
else {
return firstPropValue;
}
}

Check whether property is readonly

I have this code using reflection and I decided to replace it by FastMember.
This is my code:
var VehicleType = TypeAccessor.Create(res.GetType());
var vehicleFastMember = ObjectAccessor.Create(res);
foreach (var kvp in dictionary)
{
if (kvp.Key == "Identifier") continue;
object value = kvp.Value;
if (VehicleType.GetMembers().FirstOrDefault(prop => prop.Name == kvp.Key) != null)
{
// here inside if i want to check whether property is not readonly,
// I am afraid of runtime exception that readonly cannot be overwritten.
**if (vehicleFastMember[kvp.Key].)**
{
vehicleFastMember[kvp.Key] = kvp.Value;
}
}
}
By reflection:
That row with stars would be solved by this line:
if (property?.CanWrite ?? false)
Does FastMember offer some elegant solution too?
According to the source code of fast-member's MemberSet.Member, CanRead/CanWrite are calling the same methods you are trying to call (PropertyInfo.Can[Read/Write]).
It's also worth noting that instead of determining if the property is writable, you could simply wrap the assignment into a try/catch, and catch the ReadOnlyException that would emerge and continue with the loop.

Entity Framework: Replacing an entire DbSet collection

I have a generic class that performs add/update on entities of type T. The AddOrUpdate() method takes in a DbSet collection to act on as well as a list of items to add or update in the DbSet. The ItemExists() is used to check to see if an item already exists in the collection. If it does, we update. If not, we add. The method essentially compares the primary key of the item passed in with every single item in the table, and returns true (as well as the database object itself) if there's a match.
The code works fine for tables with small number of records. For larger tables however, the ItemExists() method is very inefficient. The method uses a foreach loop which itself is inside another foreach loop in the caller method, giving O(n^2).
An easier way would be to simply use contextDataSet.Contains(item), but that throws an exception that says Unable to create a constant value of type which makes sense since EF can't translate the class into a SQL query. So that's a no go.
Now my actual question: is there a way to replace the entire DbSet<T> with IEnumerable<T> that gets passed in? The IEnumerable that gets passed in is bound to a datagrid on the view and essentially includes all the items, so logically speaking, replacing the entire collection should be safe. Any help is greatly appreciated.
Code
public void AddOrUpdate<I, P>(Expression<Func<I, P>> dbSetExpression, IEnumerable<T> itemsToUpdate)
where I : DbContext, new()
where P : DbSet<T>
{
DataFactory.PerformOperation<I>(c =>
{
if (m_primaryKey == null && !TryFindPrimaryKey(c))
{
throw new ArgumentException("Primary key cannot be null.");
}
// Get the table name from expression passed in.
string dbsetName = ((MemberExpression)dbSetExpression.Body).Member.Name;
var propertyInfo = c.GetType().GetProperties().Single(p => p.Name == dbsetName);
// Get the values in the table.
DbSet<T> contextDataSet = propertyInfo.GetValue(c) as DbSet<T>;
foreach (var item in itemsToUpdate)
{
// If the primary key already exists, we're updating. Otherwise we're adding a new entity.
T existingItem;
if (ItemExists(contextDataSet, item, out existingItem) && existingItem != null)
{
c.Entry(existingItem).CurrentValues.SetValues(item);
}
else
{
contextDataSet.Add(item);
}
}
c.SaveChanges();
});
}
private bool ItemExists(DbSet<T> itemInDbSet, T itemInList, out T existingItem)
{
foreach (var dbItem in itemInDbSet)
{
// Get the primary key value in the database.
var dbValue = dbItem.GetType().GetProperties().Single(
p => p.Name == m_primaryKey).GetValue(dbItem);
// Get the primary key value from the item passed in.
var itemValue =
itemInList.GetType().GetProperties().Single(
p => p.Name == m_primaryKey).GetValue(itemInList);
// Compare the two values.
if (dbValue.ToString() == itemValue.ToString())
{
existingItem = dbItem;
return true;
}
}
existingItem = null;
return false;
}

possible null assignment in Dictionary Contains Key

Getting a warning on "!redirectsDictionary.ContainsKey(autoRedirect.Key)"
asp.net possible null assignment to entity marked with "notnull" attribute.
Just wondering what that's about ?
private static readonly Dictionary<string, Redirect> AutoRedirectsDictionary = new Dictionary<string, Redirect>();
foreach (var r in db.SelectItems("fast:/sitecore/content/Redirects Root//*[##templatename='Auto Redirect']"))
{
GenerateRedirects(Context.Database.GetItem(r.Fields["Root Node"].Value), r["URL Prefix"]);
foreach (var autoRedirect in AutoRedirectsDictionary)
{
if (!string.IsNullOrEmpty(autoRedirect.Key) & !redirectsDictionary.ContainsKey(autoRedirect.Key))
{
//Add to dictionary
redirectsDictionary.Add(autoRedirect.Key, autoRedirect.Value);
}
}
}
public static void GenerateRedirects(Item redirectFolder, string urlPrefix)
{
if (redirectFolder == null)
return;
var childList = redirectFolder.GetChildren();
foreach (Item child in childList)
{
if (Utilities.HasFieldValue(child, FieldToFind))
{
var shortcutUrl = urlPrefix + child.Fields[FieldToFind].Value.ToLower();
if (!string.IsNullOrEmpty(shortcutUrl) && !AutoRedirectsDictionary.ContainsKey(shortcutUrl))
{
AutoRedirectsDictionary.Add(shortcutUrl,
new Redirect(String.Empty, child, true));
}
}
else
{
GenerateRedirects(child, urlPrefix);
}
}
}
It might have to do with your use of the single & operator. A single & will not use short-circuiting to bypass a statement but will instead choose the path to execute after all expressions have been evaluated. So even though you're checking !string.IsNullOrEmpty(autoRedirect.Key) prior to the ContainsKey call, both expressions will be evaluated first and then the path of execution will be decided.
Edited as I realized I didn't truly answer your specific question (and you may already know this) but !redirectsDictionary.ContainsKey(autoRedirect.Key) will throw an exception if the key is null. Since the datatype for the key is a string there is a possibility it will throw an exception if it is null, hence the warning.

Categories

Resources