问题
I have an ad-hoc reporting system; I have no compile-time knowledge of the source type of queries or of the required fields. I could write expression trees at runtime using the System.Linq.Expressions.Expression
factory methods, and invoke LINQ methods using reflection, but Dynamic LINQ is a simpler solution.
The reporting system is to allow for queries which returns the result of a LEFT JOIN. There are fields in the joined table which are NOT NULL
in the database; but because this is a LEFT JOIN
, those fields will contain NULL
for certain records. The EF6-generated expression falls on this, because the expression projects to a non-nullable value type.
If I was doing this in compile-time LINQ, I would explicitly cast to the nullable type:
enum Color { Red, Green, Blue }
// using System;
// using static System.Linq.Enumerable;
// using System.Linq;
var range = Range(0, 3).Select(x => (Color)x).AsQueryable();
var qry = range.Select(x => (Color?)x);
Dynamic LINQ supports explicit conversions:
// using static System.Linq.Dynamic.Core
var qry1 = range.Select("int?(it)");
but only a specific set of types can be referenced in the query. If I try to use Color
in the query:
var qry2 = range.Select("Color(it)");
I get the following error:
No applicable method 'Color' exists in type 'Color'
and if I try to explicitly cast to Color?
:
var qry3 = range.Select("Color?(it)");
I get:
Requested value 'Color' was not found.
How can I do this using the Dynamic LINQ library?
回答1:
Dynamic LINQ provides a Cast
method which can be used as follows:
var range = Enumerable.Range(0,3).Select(x => (Color)x).AsQueryable();
var castDynamic = range.Cast(typeof(Color?)).ToDynamicArray();
castDynamic.Dump();
You can also pass a string with the name of the output type. Note that for nullable types, you need the full name of the type:
string s = typeof(Color?).FullName;
s.Dump();
var castDynamicFromString = range.Cast(s);
castDynamicFromString.Dump();
Cast
can also be used within the Dynamic LINQ string expression, either passing in a Type
object as a parameter, or by using the name directly:
var castInSelect = range.Select($@"Cast(""{s}""").ToDynamicArray();
castInSelect.Dump();
Output in LINQPad:
回答2:
Try this:
var arg0 = Expression.Parameter(typeof(Color), "x");
var expr = DynamicExpressionParser.ParseLambda(new[] { arg0 }, typeof(Color?), "x");
var qry2 = range.AsQueryable().Select("@0(it)", expr);
Also see https://github.com/StefH/System.Linq.Dynamic.Core/wiki/Dynamic-Expressions#dynamic-lambda-invocation
回答3:
I would like to suggest that you can use the following snippet to identify whether the input number is part of the enumeration.
public static bool IsValuePresent<T>(int number) where T : Enum
{
return !Enum.GetValues(typeof(T)).Cast<int>().Contains(number);
}
The following might be the calls to check if the input value is present in the enumeration
EnumerationConverter.IsValuePresent<Color>(99); //returns False
EnumerationConverter.IsValuePresent<Color>(2); //returns True
Once a False is obtained in the above statements, we can set the value to be null or else, we can do a simple generic type converter from integer to the enumeration like the code given below.
public static T GetAsEnum<T>(int number) where T : Enum
{
if (Enum.IsDefined(typeof(T), number))
{
return (T)Enum.ToObject(typeof(T), number);
}
return default(T);
}
Based on the understanding that you have to check for valid enumeration value and then do a conversion, the above solution can be tried, incase of any other aspect, please post here to update.
来源:https://stackoverflow.com/questions/60692533/convert-my-value-type-to-nullable-equivalent