2016-03-23 5 views
0

я регулярно нахожу такие вещи, как кодированиеПреобразования Ненулевые выражения NULLABLE без жесткого ввода/литье в LINQ запросы

int? min = someQueryable 
    .Where(x => someCondition) 
    .Select(x => (int?)x.someNonNullableIntProperty) 
    .Min(); 

Это позволяет мне получить нуль вместо «System.ArgumentNullException: значение не может быть null ", когда ничего не соответствует условию. (И я не хочу получать 0 вместо null.)

Я хотел бы избежать приведения. Это какой-то другой способ, который я пропустил?

Жесткая часть, она должна быть достаточно распространенной, чтобы быть понятной или игнорируемой поставщиками linq. ( и для моих конкретных потребностей.)

В противном случае я бы добавить некоторые пользовательские AsNullable расширения для LINQ-к-пи провайдера (который требует некоторой работы), и я не знаю, если EF (6) позволяет сделай это.

Почему я не хочу избегать приведения? Кастинг может остаться незамеченным во время рефакторинга, а затем может вызвать ошибки. И большинство из тех, что я вижу в коде, из-за небрежных разработчиков, не пытаясь запомнить, они могут иметь ненулевое значение от .Value по типам с нулевым значением, например. (Существует также тройной случай как someCondition ? someIntProperty : (int?)null но оператор «Элвис» ?., вероятно, позволит избежать большинства из них. Это может быть может даже использоваться для моего примера запроса, но это не было бы общим решением.)

Пытается new Nullable<int>(x.someNonNullableIntProperty)as suggested here не работает с NotSupportedException (с NH, не проверен с EF). Во всяком случае, это меня не устраивало. В случае более позднего изменения типа свойства он может остаться незамеченным также из-за неявного преобразования. (И пытается new Nullable(x.someNonNullableIntProperty) не компилируется, общий аргумент типа умозаключения не очень хорошо работает с конструкторами.)

Попытка x.someNonNullableIntProperty as int? (который до сих пор литые, но менее терпимы о типах несовпадения в этом случае см this here) завершается с ArgumentException (NH проверено снова, Выражение типа 'System.Int32' не может использоваться как тип 'System.Nullable`1 [System.Int32]' (перевод)).

+0

Если это простой синтаксис метода верхнего уровня 'Select', как в примере, я думаю, что-то можно сделать. Но в целом вы не можете этого сделать. –

ответ

-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 для ссылок.

+0

Вместо того, чтобы переопределять min, max и все остальные, я бы определил некоторый 'AsNullable' и расширил linq-to-nh с ним, как я уже сделал это [здесь] (/ a/35492489/1178314) для другого предмета. Но я думаю, что мы действительно должны иметь некоторый встроенный способ .Net, чтобы получить значение NULL от любого недействительного выражения 'struct' без явного упоминания его базового типа. Похоже, что это недостающая особенность. –

+0

[Выполнено] (/ a/36211132/1178314). Но не принимая этого, он не рассматривает случай EF. –

0

Для платформы Entity я отказался. Для меня это выглядит слишком много. (См. here.)

Для NHibernate, я сделал расширение , о котором я думал.

Это соответствует той же логике, что и мои другие расширения here и here.

Во-первых, определить 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 узел под 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(); 
Смежные вопросы