我经常发现自己编写了像
这样的内容usernameInput
当没有任何条件匹配时,它允许我得到null而不是“System.ArgumentNullException:value不能为null”。 (而且我不想让int? min = someQueryable
.Where(x => someCondition)
.Select(x => (int?)x.someNonNullableIntProperty)
.Min();
代替0
。)
我想避免演员。这是我错过的其他任何方式吗?
困难的部分是,linq提供者应该对其进行本地理解(或忽略)。 (linq-to-entities和linq-to-nhibernate用于满足我的特定需求。)
否则我将不得不向linq-to-nh提供者添加一些自定义null
扩展(这需要一些工作),我不知道EF(6)是否允许这样做。
为什么我希望避免演员?重构过程中可能会忽略转换,然后可能会导致错误。 我在代码中看到的大部分演员都是由于粗心的开发人员,而不是试图记住他们可以在可空类型上使用AsNullable
的非空值,例如。 (还有像.Value
这样的三元案例,但“猫王”操作符someCondition ? someIntProperty : (int?)null
可能会避免大部分操作;我们也可以编写?.
,尽管它有点冗长。default(int?)
甚至可以用于我的示例查询,但它不是一般解决方案。)
尝试?.
as suggested here失败,new Nullable<int>(x.someNonNullableIntProperty)
(使用NH,未使用EF测试)。无论如何,它也不适合我。如果稍后对属性进行类型更改,则由于隐式转换,它可能也会被忽略。 (并且尝试NotSupportedException
不能编译,泛型类型参数推断对构造函数不起作用。)
尝试new Nullable(x.someNonNullableIntProperty)
(仍然是演员,但在这种情况下对类型不匹配的容忍度较低,请参阅this here)失败并显示x.someNonNullableIntProperty as int?
(NH再次测试, Expression类型'System.Int32'不能用作'System.Nullable`1 [System.Int32]'返回类型(已翻译))。
答案 0 :(得分:-1)
I tried this once,但IEnumerable
,并提出了
public static T? max<T>(IEnumerable<T> values) where T: struct, IComparable<T>
{
T? result = null;
foreach (var v in values)
if (!result.HasValue || (v.CompareTo(result.Value) > 0))
result = v;
return result;
}
要处理IQueryable
,您需要扩展数据访问库。在NHibernate的情况下,该机制称为HqlGenerator
。有关链接,请参阅this answer。
答案 1 :(得分:-1)
对于实体框架,我放弃了。对我来说这看起来太费劲了。 (见here。)
对于NHibernate,我已经完成了我想到的AsNullable
扩展。
首先,定义AsNullable
扩展名:
public static class NullableExtensions
{
public static T? AsNullable<T>(this T value) where T : struct
{
// Allow runtime use.
// Not useful for linq-to-nhibernate, could be:
// throw NotSupportedException();
return value;
}
}
然后,实现其HQL
翻译(最初基于NHibernate compare implementation,然后非常简化,请参阅编辑):
public class AsNullableGenerator : BaseHqlGeneratorForMethod
{
public AsNullableGenerator()
{
SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition(() => NullableExtensions.AsNullable(0))
};
}
public override HqlTreeNode BuildHql(MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor)
{
// Just have to transmit the argument "as is", HQL does not need a specific call
// for null conversion.
return visitor.Visit(arguments[0]).AsExpression();
}
}
使用您的生成器扩展默认的linq2NH注册表:
public class ExtendedLinqToHqlGeneratorsRegistry :
DefaultLinqToHqlGeneratorsRegistry
{
public ExtendedLinqToHqlGeneratorsRegistry()
: base()
{
this.Merge(new AsNullableGenerator());
}
}
现在配置NH以使用新的注册表。使用hibernate.cfg.xml,在property
node:
session-factory
节点
<property name="linqtohql.generatorsregistry">YourNameSpace.ExtendedLinqToHqlGeneratorsRegistry, YourAssemblyName</property>
或者使用代码:
using NHibernate.Cfg;
// ...
var cfg = new Configuration();
cfg.LinqToHqlGeneratorsRegistry<ExtendedLinqToHqlGeneratorsRegistry>();
// And build the session factory using this configuration.
现在我们可以重写查询。
int? min = someQueryable
.Where(x => someCondition)
.Select(x => x.someNonNullableIntProperty.AsNullable())
.Min();