Ekspresja.GreaterThan fails, if one operand is nullable type, other is non-nullable
Tworzę jakiś dynamiczny linq i mam problemy z następującym wyjątkiem:
Operator binarny GreaterThanOrEqual nie jest zdefiniowana dla typów "System.Nullable " 1 [System.DateTime]" i ' System.DateTime "
Rozumiem dlaczego, ponieważ mój typ pola jest nullable i mi przechodzi w DateTime.W zasadzie teraz.
Więc próbując rozwiązać ten problem próbowałem
System.Nullable<DateTime> now;
now = DateTime.Now;
Ale wynikowy typ jest obiektem Nie nullable i dlatego nadal daje mnie powyższy wyjątek.
Jakieś sugestie?!Update: dla lepszego wyjaśnienia teraz zmienna staje się typem Nie-nullable, gdy jest ustawiona, a nie pozostaje jako nullable DateTime, więc dopasowanie rzuca wyjątek
Aktualizacja: rzeczywisty kod można zobaczyć w projekcie CodePlex:
Http://webquarters.codeplex.com/SourceControl/changeset/view/36529#574700
Linia jest ~145
fExp = Expression.GreaterThanOrEqual(fExpLeft, fExpRight);
5 answers
Problem polega na tym, że biblioteka wyrażeń rzuca wyjątek, gdy podano dwa argumenty o niedopasowanej nullability. Oto prosty repro:
Expression<Func<DateTime?>> ex1 = ()=>DateTime.Now;
Expression<Func<DateTime>> ex2 = ()=>DateTime.Now;
var ex3 = Expression.GreaterThan(ex1.Body, ex2.Body);
Nie jest dla mnie jasne, czy jest to błąd, czy nie; reguły W C# wymagają, aby w tym scenariuszu operand nie-nullable był konwertowany do nullable, a porównywana jest forma lifted-to-nullable. jednak, biblioteka drzewa wyrażeń jest nie wymaga przestrzegania zasad C # ponieważ oczywiście biblioteka drzewa wyrażeń może być używana do reprezentowania wyrażeń C#, wyrażeń Pythona, wyrażeń JScript, wyrażeń VB i tak dalej; nie może być zgodna ze wszystkimi regułami każdego możliwego języka.
Ale niezależnie od tego, wygląda na to, że to może być błąd, więc prześlę go do zespołu drzewa wyrażeń i zobaczę, co powiedzą. W międzyczasie możesz łatwo obejść go, definiując własną metodę pomocniczą, która naprawia operandy. Szybki szkic be:
static Expression MyGreaterThan(Expression e1, Expression e2)
{
if (IsNullableType(e1.Type) && !IsNullableType(e2.Type))
e2 = Expression.Convert(e2, e1.Type);
else if (!IsNullableType(e1.Type) && IsNullableType(e2.Type))
e1 = Expression.Convert(e1, e2.Type);
return Expression.GreaterThan(e1, e2);
}
static bool IsNullableType(Type t)
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
Zauważ jednak, że to nie sprawdza, czy typy e1 i e2 różnią się tylko pod względem nullability; jeśli przekażesz nullable int i non-nullable double expression, złe rzeczy się zdarzają. zostawiam to jako ćwiczenie, aby zaimplementować lepszą logikę, która sprawdza, czy oba wyrażenia są typu, który różni się tylko nullability.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 20:07:47
Gdzie jest twoje porównanie, Zmień porównanie TAK:
(nullableDT >= DT)
Do
(nullableDT != null && nullableDT.Value >= DT)
Edit:
Zgodnie z Twoim komentarzem, napisz funkcję, która zajmuje 2 obiekty, wewnątrz funkcji sprawdź, czy są to typy null, i sprawdź, czy są null, a następnie porównaj wartości. Ta funkcja będzie prawdopodobnie używać kodu podobnego do ^.
To zaczyna brzmieć, jakbyś miał większy problem. Albo otrzymujesz nieprawidłowe dane, (tj. Twój kod w innym miejscu zwraca dane że nie powinno być, nie problem w kodzie, ale problem w logice,) lub twoje założenie, że możesz porównać te obiekty jest nieprawidłowe. Co po raz kolejny jest błędem logicznym. Myślę, że powinieneś się na chwilę cofnąć. Jeśli dodasz więcej kodu, może będziemy w stanie Ci pomóc.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 19:24:03
Znalazłem rozwiązanie, które działa w. Net framework. Tutaj metoda, która jest nadal w toku, ale działa i działa dla LINQ do encji:
public PagedViewModel<T> Filter<TValue>(Expression<Func<T, TValue>> predicate, FilterType filterType = FilterType.Equals) {
var name = (predicate.Body as MemberExpression ?? ((UnaryExpression)predicate.Body).Operand as MemberExpression).Member.Name;
var value = Expression.Constant(ParamsData[name].To<TValue>(), typeof (T).GetProperty(name).PropertyType);
// If nothing has been set for filter, skip and don't filter data.
ViewData[name] = m_QueryInternal.Distinct(predicate.Compile()).ToSelectList(name, name, ParamsData[name]);
if (string.IsNullOrWhiteSpace(ParamsData[name]))
return this;
var nameExpression = Expression.Parameter(typeof(T), name);
var propertyExpression = Expression.Property(nameExpression, typeof(T).GetProperty(name));
// Create expression body based on type of filter
Expression expression;
MethodInfo method;
switch(filterType) {
case FilterType.Like:
method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
expression = Expression.Call(propertyExpression, method, value);
break;
case FilterType.EndsWith:
case FilterType.StartsWith:
method = typeof(string).GetMethod(filterType.ToString(), new[] { typeof(string) });
expression = Expression.Call(propertyExpression, method, value);
break;
case FilterType.GreaterThan:
expression = Expression.GreaterThan(propertyExpression, value);
break;
case FilterType.Equals:
expression = Expression.Equal(propertyExpression, value);
break;
default:
throw new ArgumentException("Filter Type could not be determined");
}
// Execute the expression against Query.
var methodCallExpression = Expression.Call(
typeof (Queryable),
"Where",
new[] { Query.ElementType },
Query.Expression,
Expression.Lambda<Func<T, bool>>(expression, new[] { nameExpression }));
// Filter the current Query data.
Query = Query.Provider.CreateQuery<T>(methodCallExpression);
return this;
}
To, co się tutaj dzieje, to wartość stała wyrażenia jest rzucana do typu predykatu. Dla jasności metoda ta jest wywoływana przez:
var paramsData = new NameValueCollection { { "CreatedOn", DateTime.Today.ToString() } };
var model = m_data.ToPagedList(new ViewDataDictionary(), paramsData, 1, 10, null, x => x.LastName)
.Filters(Criteria<TrainerProfile>.New(x => x.CreatedOn, FilterType.GreaterThan))
.Setup();
Ten kod jest oparty na MVC 3, stąd niektóre z ParamsData [""], które jest (Request.Params).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-07 04:10:05
Nie jestem pewien dokładnie jaki jest Twój kod, ale aby uzyskać nie-nullable wersję Nullable, zadzwoń do jego .Value
członka.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 18:28:53
To zabawne.]}
Próbowałem obu wariantów tego kodu:
System.Nullable<DateTime> now = new System.Nullable<DateTime>();
now = DateTime.Now;
I
System.Nullable<DateTime> now;
now = DateTime.Now;
I obie pracowały bez błędów.
Więc ponownie przeczytałem twoje pytanie. W rzeczywistości odpowiedź jest nadal na właściwości "Value". Inicjalizacja zmiennej if fine, but if you do:(now > = DateTime.Teraz) w zapytaniu Linq pojawi się błąd. Powinno być (teraz.Wartość > = DateTime.Teraz)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 19:54:12