将我的值类型转换为可空等效类型

6
我有一个临时报告系统,我没有编译时了解查询源类型或所需字段的知识。我可以使用System.Linq.Expressions.Expression工厂方法在运行时编写表达式树,并使用反射调用LINQ方法,但动态LINQ是一个更简单的解决方案。
报告系统允许查询返回LEFT JOIN的结果。连接表中的某些字段在数据库中为NOT NULL,但由于这是一个LEFT JOIN,对于某些记录,这些字段将包含NULL。EF6生成的表达式因此失败,因为该表达式投影到非可空值类型。
如果我在编译时使用LINQ来完成这个任务,我会显式地转换为可为空类型:
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);

动态 LINQ 支持显式转换:

// using static System.Linq.Dynamic.Core

var qry1 = range.Select("int?(it)");

但是查询中只能引用特定的一组类型。如果我尝试在查询中使用Color

var qry2 = range.Select("Color(it)");

我收到了以下错误提示:

在类型 'Color' 中不存在可用的方法 'Color'

如果尝试将其显示转换为 Color?,则会出现此问题:
var qry3 = range.Select("Color?(it)");

我收到的是:

请求的值 'Color' 未找到。

如何使用动态LINQ库完成此操作?

在枚举中添加一个“NONE”的项目。 - jdweng
最终我需要从左连接中的所需列获取值,如果联接表中没有相应记录,则这些值将为 NULL。此外,这对于非枚举值类型也没有帮助。 - Zev Spitz
你是将枚举名称存储在数据库中还是将整数值存储在数据库中? - Saravanan
@Saravanan 整数值。 - Zev Spitz
你需要使用DLINQ吗? - picolino
显示剩余3条评论
3个回答

7

动态 LINQ 提供了一个 Cast 方法,可以按如下方式使用:

var range = Enumerable.Range(0,3).Select(x => (Color)x).AsQueryable();
var castDynamic = range.Cast(typeof(Color?)).ToDynamicArray();
castDynamic.Dump();

你也可以传递一个输出类型名称的字符串。请注意,对于可空类型,需要使用完整的类型名称:

string s = typeof(Color?).FullName;
s.Dump();
var castDynamicFromString = range.Cast(s);
castDynamicFromString.Dump();

Cast也可以在动态LINQ字符串表达式中使用,可以通过将Type对象作为参数传入,或直接使用名称来实现:

var castInSelect = range.Select($@"Cast(""{s}""").ToDynamicArray();
castInSelect.Dump();

LINQPad的输出:

在此输入图片描述


你需要提供包括命名空间在内的Color枚举的完整名称。或者你可以尝试提供备用配置并将ResolveTypesBySimpleName设置为true。请参阅https://github.com/StefH/System.Linq.Dynamic.Core/wiki/Configuration了解如何进行操作。 - Stef Heyenrath
看起来不错。谢谢。 - Stef Heyenrath

3

0
我想建议您可以使用以下代码片段来确定输入的数字是否是枚举的一部分。
public static bool IsValuePresent<T>(int number) where T : Enum
{
    return !Enum.GetValues(typeof(T)).Cast<int>().Contains(number);
}

以下可能是用于检查输入值是否存在于枚举中的调用

EnumerationConverter.IsValuePresent<Color>(99); //returns False
EnumerationConverter.IsValuePresent<Color>(2); //returns True

在上述语句中,一旦获得False,我们可以将值设置为null,或者我们可以执行一个简单的通用类型转换器,从整数转换为枚举,如下所示的代码。

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);
}

根据您需要检查有效枚举值并进行转换的理解,可以尝试上述解决方案。如果有其他方面需要,请在此处发布更新。


问题不在于确定输入值是否是枚举的一部分。问题(主要)在于据我所知,动态LINQ库没有办法说“将此转换为任意类型”,只有识别的类型。没有动态LINQ,这很容易做到:Range(0, 3).Select(x => (Color)x).Select(x => (Color?)x),即使值恰好不匹配任何枚举值,也不会出现运行时错误。 - Zev Spitz
好的,您是想使用动态LINQ,以便将其应用于对数据库的筛选查询吗? - Saravanan
你是否打算做类似于这个链接的事情:https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions.expression.convert?view=netframework-4.8 - Saravanan
是的。我正在编写一个带有LEFT JOIN的查询,加入表中的某些行返回NULL。加入表中的某些字段被定义为非空值类型;但是当这些字段在LEFT JOIN中返回时,它们可以为NULL。当向Dynamic LINQ传递字符串时,它创建的表达式不考虑这些NULL值;Dynamic LINQ识别的类型可以“可空化”,但其他类型,如自定义枚举,则不能。我知道我可以重写生成的表达式树并修复它,但如果可能的话,我更愿意在Dynamic LINQ的限制内工作。 - Zev Spitz
我已经澄清了问题。 - Zev Spitz
请注意,此答案与问题无关(如果它曾经有过)。 - Zev Spitz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接