Create null instead of empty dictionary where IEnumerable has no data - c#

Following is my Linq code, here we return a Dictionary<string,object>, while filling object value for each string key we internally fill the Dictionary<string,string>, which is FieldPropertyMappingin the code below
allCardColumnMappingFieldPropertyEntity
.GroupBy(column => (column.FieldDesc + column.OutputColumnId))
.ToDictionary(groupElement => groupElement.Key,
groupElement => (object)groupElement.Select(currentElement =>
{
currentElement.FieldPropertyMapping =
fieldPropertyEntity
.Where(field => field.OutputColumnId == currentElement.OutputColumnId)
.Where(field => field.FieldDesc == currentElement.FieldDesc)
.ToDictionary(property => property.PropertyDesc, property => property.PropertyValue);
return currentElement;
}).FirstOrDefault());
Challenge we have in the current code we cannot have currentElement.FieldPropertyMapping as null at any time even if the Where clause find no matching record it will always lead to empty dictionary, but we have a business requirement to make it null if it is empty. I have done the following modification to fulfill the condition, but probably there's a better way to achieve it in Linq code, any pointer / suggestion
Modified code
allCardColumnMappingFieldPropertyEntity
.GroupBy(column => (column.FieldDesc + column.OutputColumnId))
.ToDictionary(groupElement => groupElement.Key,
groupElement => (object)groupElement.Select(currentElement =>
{
List<DesignCardFieldProperty> localDesignCardFieldPropertyEntity = fieldPropertyEntity
.Where(field => field.OutputColumnId == currentElement.OutputColumnId)
.Where(field => field.FieldDesc == currentElement.FieldDesc).ToList();
if(localDesignCardFieldPropertyEntity.Any())
currentElement.FieldPropertyMapping = localDesignCardFieldPropertyEntity
.ToDictionary(property => property.PropertyDesc, property => property.PropertyValue);
return currentElement;
}).FirstOrDefault());

Try with simple extension method
public static class Extensions
{
public static IDictionary<TKey, TValue> NullIfEmpty<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null || !dictionary.Any())
{
return null;
}
return dictionary;
}
}

Related

Good way to handle NullReferenceException before C# 6.0

My code below gives me a NullReferenceException and the stack trace tells me the problem is in the Count method, so I'm pretty sure at some point foo, bar or baz is null.
My code:
IQueryable<IGrouping<string, Referral>> queryable= ...;
var dict = queryable.ToDictionary(g => g.Key.ToString(),
g => g.Count(r => r.foo.bar.baz.dummy == "Success"));
I'm wondering what's a concise way to handle null cases.
I learn that in C# 6.0 I can just do foo?.bar?.baz?.dummy, however the project I'm working on isn't C# 6.0
A solution for <6.0 would be:
.Count(r => r.foo != null &&
r.foo.bar != null &&
r.foo.bar.baz != null &&
r.foo.bar.baz.dummy == "Success")
Exactly for complex constructs like the one above the null propagation operator was introduced.
Furthermore you could also refactor the expression into a private method:
private Expression<Func<Referral, bool>> Filter(string value)
{
return r => r.foo != null &&
r.foo.bar != null &&
r.foo.bar.baz != null &&
r.foo.bar.baz.dummy == value;
}
and use it as follows:
g => g.Count(Filter("Success"))
You can use the following extension methods.
public static TResult With<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
where TResult : class
where TInput : class
{
return o == null ? null : evaluator(o);
}
public static TResult Return<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator, TResult failureValue)
where TInput : class
{
return o == null ? failureValue : evaluator(o);
}
Their combination gives you a nice, readable API for handling nulls:
return foo
.With(o => o.bar)
.With(o => o.baz)
.Return(o => o.dummy, null);
The problem is that the ToDictionary method isn't actually done on the queryable - instead, you get the whole collection, and do the aggregation in your application, rather than on the DB server.
So instead of using ToDictionary directly, use Select first:
IQueryable<IGrouping<string, Referral>> queryable= ...;
var dict = queryable.Select(g => new { Key = g.Key.ToString(),
Count = g.Count(r => r.foo.bar.baz.dummy == "Success") })
.ToDictionary(i => i.Key, i => i.Count);
This will make sure the aggregation is done in the database (where you don't care about those nulls) and not in the C# code (where you get a NullReferenceException).
Of course, this assumes that the queryable you're using is a DB query (or, to be more precise, a queryable that supports aggregation and has ANSI SQL-like NULL semantics). If you have a different custom queryable, it's not going to help (unless you explicitly add those NULL semantics yourself).
// if null, use null
if(objectvariable == null)
{
// throw exception
}
// if not null
if(objectvariable != null)
{
// continue
}

How to get single column with LINQ in anonymous method

How to get single column with anonymous method using linq expression. Here's my code and it doesn't work:
public IEnumerable<object> GetPropertyValues<T>(string propName) where T : class
{
return base.Query<T>().AsEnumerable()
.Where(x => x.GetType().GetProperty(propName).Name == propName)
.Select(x => x.GetType().GetProperty(propName).GetValue(x, null));
}
Here's the code in non generic method:
base.Query<Product>().Select(x => x.ProductName).AsEnumerable();
Thanks in advance.
This condition is incorrect, because when the property propName is missing, it crashes, rather than returning false:
.Where(x => x.GetType().GetProperty(propName).Name == propName)
If you wanted to say "the dynamic type has the property propName", a proper condition for it would look like this:
.Where(x => x.GetType().GetProperty(propName) != null)
Note that this is necessary only when some, but not all, subclasses of T have the desired property propName. If the property is present in the T itself, you could get property upfront, and do the rest of the query like this:
var theProp = typeof(T)..GetProperty(propName);
return base.Query<T>().AsEnumerable().Select(x => theProp.GetValue(x, null));
Once you call AsEnumerable() all further filtering will happen in memory rather than SQL. Thus, you could do:
var prop = typeof(T).GetProperty(propName);
return base.Query<T>().AsEnumerable().Select(t => prop.GetValue(t));
but this will select all columns of T into memory before filtering down to just one. To do the filtering in SQL, you'll need to dynamically construct an expression:
var prop = typeof(T).GetProperty(propName);
var parameter = Expression.Parameter(typeof(T));
var memberAccess = Expression.MakeMemberAccess(parameter, prop);
var selector = Expression.Lambda<Func<T, TProperty>>(memberAccess, parameter);
return base.Query<T>().Select(selector);
For an enumeration of any type of objects...
public static IEnumerable<Object> Pluck<Object>(this IEnumerable<Object> source, string propName)
{
return source
.Select(x => new {
obj: x
prop: x.GetType().GetProperty(propName)
})
.Where(x => x.prop != null)
.Select(x => x.prop.GetValue(x.obj));
};
For an enumeration of a single type of object...
public static IEnumerable<Object> Pluck<T>(this IEnumerable<T> source, string, propName)
{
var prop = typeof(T).GetProperty(propName);
return prop == null
? Enumerable.Empty<Object>()
: source.Select(x => prop.GetValue(x));
};

Handling Nulls in a model passed over into a controller

I am trying to fix these linq statements so that they wont error if the model is null.
For example:
model.FilterSet.Dispositions may be null. While model.FilterSet.GenderFilters may not.
My current linq statement gives error if there is a null so I tried to add .Where(item => item != null) in the DispositionFilters linq, but it gives me an error saying that this will always be true.
Here is my code:
var filters = new RespondentCSVFilters
{
CSVFilters = new CSVFilters
{
DispositionFilters = model.FilterSet.Dispositions.Where(item=>item != null).ToList().ConvertAll(item => (int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), item.ToString()))),
EthnicitiesFilters = model.FilterSet.Ethnicities.ConvertAll(item => (int)((Ethnicity)Enum.Parse(typeof(Ethnicity), item.ToString()))),
GenderFilters = model.FilterSet.Genders.ConvertAll(item => (int)((Gender)Enum.Parse(typeof(Gender), item.ToString()))),
SourcesFilters = model.FilterSet.Sources.ConvertAll(item => (int)((RespondentSource)Enum.Parse(typeof(RespondentSource), item.ToString()))),
}
};
I am not sure how to make the changes in order to handle nulls.
I tried to add .Where(item => item != null)
But this will only check each item for null, not the source ("Dispositions"). To check for null fluently (without adding a bunch of "if" statements), an EmptyIfNull extension can be helpful:
public static class Extensions
{
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> self)
{
return self == null ? Enumerable.Empty<T>() : self;
}
}
Now you can write ...
model.FilterSet.Dispositions.EmptyIfNull().Where( ... )
and if model.FilterSet.Dispositions is null, you won't get an exception, but the statement will evaluate to an empty set.

Re-use parts of a linq query

We have a program that uses Linq-To-Sql and does a lot of similar queries on our tables. In particular, we have a timestamp column, and we pull the most recent timestamp to see if the table has changed since our last query.
System.Data.Linq.Binary ts;
ts = context.Documents
.Select(r => r.m_Timestamp)
.OrderByDescending(r => r)
.FirstOrDefault();
We repeat this query often on different tables that's relevant for the current form/query whatever. What I would like to do is create a "function" or something that can repeat the last 3 lines of this query (and ideally would work on every table). Here's what I would like to write:
System.Data.Linq.Binary ts;
ts = context.Documents
.GetMostRecentTimestamp();
But I have no idea how to create this "GetMostRecentTimestamp". Also, these queries are never this simple. They usually filter by the Customer, or by the current order, so a more valid query might be
ts = context.Documents
.Where(r => r.CustomerID == ThisCustomerID)
.GetMostRecentTiemstamp();
Any help? Thanks!
Update [Solution]
I selected Bala R's answer, here's the code updated so it compiles:
public static System.Data.Linq.Binary GetMostRecentTimestamp(this IQueryable<Data.Document> docs)
{
return docs
.Select(r => r.m_Timestamp)
.OrderByDescending(r => r)
.FirstOrDefault();
}
The only drawback to this solution is that I will have to write this function for each table. I would have loved Tejs's answer, if it actually worked, but I'm not re-designing my database for it. Plus DateTime is a not a good way to do timestamps.
Update #2 (Not so fast)
While I can do a query such as Documents.Where( ... ).GetMostRecentTimestamp(), this solution fails if I try to do an association based query such as MyCustomer.Orders.GetMostRecentTimestamp(), or MyCustomer.Orders.AsQueryable().GetMostRecentTimestamp();
This is actually pretty easy to do. You simply need to define an interface on the entities you wish to provide this for:
public class MyEntity : ITimestamp
Then, your extenstion method:
public static DateTime GetMostRecentTimestamp<T>(this IQueryable<T> queryable)
where T : ITimestamp
{
return queryable.Select(x => x.m_Timestamp)
.OrderByDescending(r => r)
.FirstOrDefault()
}
This is then useful on any entity that matches the interface:
context.Documents.GetMostRecentTimestamp()
context.SomeOtherEntity.GetMostRecentTimestamp()
How about an extension like this
public static DateTime GetMostRecentTimestamp (this IQueryable<Document> docs)
{
return docs.Select(r => r.m_Timestamp)
.OrderByDescending(r => r)
.FirstOrDefault();
}
Hmm...
DateTime timeStamp1 = dataContext.Customers.Max(c => c.TimeStamp);
DateTime timeStamp2 = dataContext.Orders.Max(c => c.TimeStamp);
DateTime timeStamp3 = dataContext.Details.Max(c => c.TimeStamp);
I created a pair of extension methods that could help you out: ObjectWithMin and ObjectWithMax:
public static T ObjectWithMax<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
where TResult : IComparable<TResult>
{
if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");
var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();
return elements.Aggregate(seed,
(s, x) =>
projection(x).CompareTo(s.Projection) >= 0
? new {Object = x, Projection = projection(x)}
: s
).Object;
}
public static T ObjectWithMin<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
where TResult : IComparable<TResult>
{
if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");
var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();
return elements.Aggregate(seed,
(s, x) =>
projection(x).CompareTo(s.Projection) < 0
? new {Object = x, Projection = projection(x)}
: s
).Object;
}
These two are more efficient than OrderBy().FirstOrDefault() when used on in-memory collections; however they're unparseable by IQueryable providers. You'd use them something like this:
ts = context.Documents
.Where(r => r.CustomerID == ThisCustomerID)
.ObjectWithMax(r=>r.m_Timestamp);
ts is now the object having the most recent timestamp, so you don't need a second query to get it.

Check if property is null in lambda expression

I have a list of objects that I am trying to bind to a listview. I am sorting by two properties. The problem exists whereby some records may not have one of the properties. This is causing an error. I would like it to still bind the records that have the property.
IEnumerable<ERec> list = retailerList.Cast<ERec>();
lvwRetailStores.DataSource = list.OrderByDescending(r => r.Properties["RS_Partner Type"].ToString())
.ThenBy(r => r.Properties["RS_Title"].ToString());
list.Where(r => r.Properties["RS_Partner_Type"] != null && r.Properties["RS_Title"] != null)
.OrderByDescending(r => r.Properties["RS_Partner Type"].ToString())
.ThenBy(r => r.Properties["RS_Title"].ToString());
Or instead of != null, use whatever test the Properties collection has.
I've found that the ?? Operator works well. I use Parenthesis to evaluate for null,
For Example:
Datetime? Today = DateTimeValue
// Check for Null, if Null put Today's date
datetime GoodDate = Today ?? DateTime.Now
This same logic works in Lambda, just use parenthesis to ensure that the correct comparisons are used.
You can use a ternary expression in the lambda:
list.OrderByDescending(r => r.Properties["RS_Partner_Type"] == null ? null : r.Properties["RS_Partner Type"].ToString())
.ThenBy(r => r.Properties["RS_Title"] == null ? null : r.Properties["RS_Title"].ToString());
Another common approach is to give the collection a suitable default value, and return that when the collection doesn't have a particular key. For instance, if Properties implements IDictionary,
public static class IDictionaryExtension {
public static TValue GetValue<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue default) {
TValue result;
return dict.TryGetValue(key, out result) ? result : dflt;
}
}
...
lvwRetailStores.DataSource = list.OrderByDescending(r => r.GetValue("RS_Partner Type", "").ToString())
.ThenBy(r => r.GetValue("RS_Title","").ToString());

Categories