Cadre d'Entity: LINQ to Entities prend uniquement en charge les types primitifs de modèles de données D'Entity casting

j'ai écrit une méthode pour permettre qu'une Expression soit passée pour la clause orderby, mais j'ai rencontré ce problème.

incapable de lancer le type 'Système.DateTime ' à taper 'Système.IComparable". LINQ to Entities ne prend en charge que les données de l'entité de coulée Modèle de type primitif.

Fondamentalement l'expression est ceci:

Expression<Func<K, IComparable>> orderBy

Et s'utilise comme ceci:

SomeEntities.SomeTable
.Where
(
   whereClause
)
.Select
(
   selectClause
)
.OrderBy(orderBy)

L'idée est que je puisse utiliser un dictionnaire pour tenir string correspond à des expressions comme:

_possibleSortForForumItem.Add("CreateDate", item => item.CreateDate);

alors j'ai une méthode qui prend la chaîne de tri et renvoie l'expression si elle correspond à une clé dans le dictionnaire, si non renvoie une valeur par défaut. (L'idée étant un moyen de contrôler ce qu'il peut être commandé par) Maintenant cela fonctionne pour les propriétés de chaîne, mais jusqu'à présent pas pour datetime ou integer que je reçois le message d'erreur ci-dessus.

maintenant que je comprends (vaguement) le problème est que le cadre de L'entité a besoin d'être un Primaire / EDM type parce qu'il doit convertir le C# DateTime en quelque chose que la base de données peut gérer.

y a-t-il un moyen de convertir le datetime en un type primitif pour que cela fonctionne encore?

Solution

La méthode pour obtenir l'ordre par la méthode: (Prendre dans une requête et retourner dans "ordonné")

private static Func<IQueryable<ForumViewItem>, IOrderedQueryable<ForumViewItem>> GetMethodForSort(String sortBy)
{
  if (_methodForSort == null)
  {
    _methodForSort = new Dictionary<String, Func<IQueryable<ForumViewItem>, IOrderedQueryable<ForumViewItem>>>();
    _methodForSort.Add(SortForumViewItemCreatedOn, item => item.OrderBy(innerItem => innerItem.CreatedOn));
    ...
  }

  Func<IQueryable<ForumViewItem>, IOrderedQueryable<ForumViewItem>> orderMethod;

  if(String.IsNullOrEmpty(sortBy) || !_methodForSort.ContainsKey(sortBy))
  {
    orderMethod = _methodForSort["ForumName"];
  }
  else
  {
    orderMethod = _methodForSort[sortBy];
  }

  return orderMethod;
}

la signature de la méthode pour la méthode de requête Générique:

IList<K> GetListForGrid<T, K>(this ObjectQuery<T> query, ... Func<IQueryable<K>, IOrderedQueryable<K>> orderBy, ...)

et l'utilisation des méthode:

initialQuery = query
  .Where
  (
    somethingEqualsSomething
  )
  .Select
  (
    selectClause
  );

var orderedQuery = orderBy(initialQuery);

returnValue = orderedQuery
  .Skip(numberToShow * realPage)
  .Take(numberToShow)
  .ToList();
24
demandé sur Programmin Tool 2009-07-18 01:48:59

5 réponses

le cadre Entity rend cela difficile et je ne suis pas sûr qu'il y ait un moyen de faire ce que vous voulez faire avec un seul type de valeur de retour (IComparable, object, etc). Vous pourriez envisager de retravailler votre conception dans un dictionnaire de nom -Func<IQueryable<K>, IOrderedQueryable<K>> valeurs:

_possibleSortForForumItem.Add("CreateDate", 
    query => query.OrderBy(item.CreateDate));

Et ensuite l'appliquer comme ceci:

var orderedQuery = query.OrderBy(item => item.DefaultOrderColumn);

Func<IQueryable<K>, IOrderedQueryable<K>> assignOrderBy = null;

if (_possibleSortForForumItem.TryGetValue(orderColumnName, out assignOrderBy))
{
    orderedQuery = assignOrderBy(query);
}
13
répondu Ben M 2009-07-17 22:54:51

je sais que c'est vieux, mais je cherchais à accomplir exactement la même chose que l'OP et je ne voulais pas utiliser le Func<IQueryable<T>, IOrderedQueryable<T>> dans mon dictionnaire. Surtout parce que je devrais mettre en œuvre à la fois un OrderBy et OrderByDescending délégué.

j'ai fini par créer une méthode d'extension pour IQueryable ObjectSort qui va simplement vérifier pour voir ce que le type de retour de l'expression devrait être et puis créer un nouveau lambda en utilisant ce type de sorte que LINQ aux entités ne va pas paniquer hors.

Je ne suis pas sûr que ce soit une bonne solution ou pas mais l'exemple ci-dessous fonctionne pour DateTime et int alors, espérons qu'il vous donnera quelques idées si vous cherchez à accomplir quelque chose de similaire!

public static IOrderedQueryable<T> ObjectSort<T>(this IQueryable<T> entities, Expression<Func<T, object>> expression, SortOrder order = SortOrder.Ascending)
{
    var unaryExpression = expression.Body as UnaryExpression;
    if (unaryExpression != null)
    {
        var propertyExpression = (MemberExpression)unaryExpression.Operand;
        var parameters = expression.Parameters;

        if (propertyExpression.Type == typeof(DateTime))
        {
            var newExpression = Expression.Lambda<Func<T, DateTime>>(propertyExpression, parameters);
            return order == SortOrder.Ascending ? entities.OrderBy(newExpression) : entities.OrderByDescending(newExpression);
        }

        if (propertyExpression.Type == typeof(int))
        {
            var newExpression = Expression.Lambda<Func<T, int>>(propertyExpression, parameters);
            return order == SortOrder.Ascending ? entities.OrderBy(newExpression) : entities.OrderByDescending(newExpression);
        }

        throw new NotSupportedException("Object type resolution not implemented for this type");
    }
    return entities.OrderBy(expression);
}
23
répondu Lucifer Sam 2012-02-06 03:19:53

a rencontré un problème similaire à celui de l'affiche originale, où les expressions "Order By" étaient écrites en lambdas de L'Expression de type>. Ceux-ci ont été interprétés correctement par le fournisseur de linq de NHibernate, mais la migration vers L'EF 5 s'est traduite par "impossibilité de lancer le système de type'.Système DateTime' to type'.IComparable". LINQ to Entities supporte uniquement les types primitifs de modèles de données D'entités de casting."

les méthodes suivantes fournissent une conversion à L'Expression < Func> en appelant les diverses méthodes "OrderBy" (en utilisant la réflexion - excuses...) Notez qu'ils étaient initialement encapsulés dans une classe générique OrderBy.

    private static readonly Type QueryableType = typeof(Queryable);

    // HACK: Use reflection to call strongly-typed methods instead of object-based methods
    // This is to work around "Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types."
    private IOrderedQueryable<T> ApplyOrderByTo(
        IQueryable<T> query,
        Expression<Func<T, object>> keySelector,
        bool sortAscending,
        bool useReflection)
    {
        if (useReflection)
        {
            var body = keySelector.Body as UnaryExpression;
            var keyExpr = body.Operand as MemberExpression;

            return (IOrderedQueryable<T>)query.Provider.CreateQuery(
                Expression.Call(
                QueryableType,
                sortAscending ? "OrderBy" : "OrderByDescending",
                new Type[] { typeof(T), keyExpr.Type },
                query.Expression,
                Expression.Lambda(keyExpr, keySelector.Parameters)));
        }
        else
        {
            if (sortAscending)
                return query.OrderBy(keySelector);
            else
                return query.OrderByDescending(keySelector);
        }
    }

    // HACK: Use reflection to call strongly-typed methods instead of object-based methods
    // This is to work around "Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types."
    private IOrderedQueryable<T> ApplyOrderByTo(
        IOrderedQueryable<T> query,
        Expression<Func<T, object>> keySelector,
        bool sortAscending,
        bool useReflection)
    {
        if (useReflection)
        {
            var body = keySelector.Body as UnaryExpression;
            var keyExpr = body.Operand as MemberExpression;

            return (IOrderedQueryable<T>)query.Provider.CreateQuery(
                Expression.Call(
                QueryableType,
                sortAscending ? "ThenBy" : "ThenByDescending",
                new Type[] { typeof(T), keyExpr.Type },
                query.Expression,
                Expression.Lambda(keyExpr, keySelector.Parameters)));
        }
        else
        {
            if (sortAscending)
                return query.ThenBy(keySelector);
            else
                return query.ThenByDescending(keySelector);
        }
    }
4
répondu OldNic 2013-05-13 14:21:45

j'ai trouvé une solution très simple à votre problème (et le mien aussi). Lorsque vous créez votre expression de recherche, vous devez passer sur le type de propriété (vous la connaissez alors) mais stocker l'expression dans la variable dynamique:

Expression<Func<TObject, DateTime>> Expr = obj=>obj.StartDate;
dynamic dynExpr=Expr;

maintenant vous pouvez stocker dynExpr dans une table ou n'importe où vous voulez, avec des expressions int, des expressions string, ... et quand le moment est venu, vous pouvez l'utiliser dans la méthode OrderBy. Mais pas de manière standard (méthode d'extension):

query=query.OrderBy(dynExpr);

, seulement ce façon:

query=Queryable.OrderBy(query, dynExpr);

de cette façon, vous pouvez utiliser une expression dans toutes les fonctions de tri (OrderBy, OrderByDescending, ThenBy, ThenByDescending).

2
répondu Wojciech Mikołajewicz 2015-08-18 15:19:54

avec l'inspiration D'OldNic j'ai créé quelques méthodes d'extension pour le tri par les membres. Il fonctionne très bien pour moi. Je suis également en utilisant le Système.Données.SqlClient.SortOrder enum pour définir l'ordre de tri.

        /// <summary>
    ///     Supports sorting of a given member as an expression when type is not known. It solves problem with LINQ to Entities unable to
    ///     cast different types as 'System.DateTime', 'System.DateTime?' to type 'System.Object'.
    ///     LINQ to Entities only supports casting Entity Data Model primitive types.
    /// </summary>
    /// <typeparam name="T">entity type</typeparam>
    /// <param name="query">query to apply sorting on.</param>
    /// <param name="expression">the member expression to apply</param>
    /// <param name="sortOrder">the sort order to apply</param>
    /// <returns>Query with sorting applied as IOrderedQueryable of type T</returns>
    public static IOrderedQueryable<T> OrderByMember<T>(
        this IQueryable<T> query, 
        Expression<Func<T, object>> expression, 
        SortOrder sortOrder)
    {
        var body = expression.Body as UnaryExpression;

        if (body != null)
        {
            var memberExpression = body.Operand as MemberExpression;

            if (memberExpression != null)
            {
                return
                    (IOrderedQueryable<T>)
                    query.Provider.CreateQuery(
                        Expression.Call(
                            typeof(Queryable), 
                            sortOrder == SortOrder.Ascending ? "OrderBy" : "OrderByDescending",
                            new[] { typeof(T), memberExpression.Type }, 
                            query.Expression,
                            Expression.Lambda(memberExpression, expression.Parameters)));
            }
        }

        return sortOrder == SortOrder.Ascending ? query.OrderBy(expression) : query.OrderByDescending(expression);
    }

    /// <summary>
    ///     Supports sorting of a given member as an expression when type is not known. It solves problem with LINQ to Entities unable to
    ///     cast different types as 'System.DateTime', 'System.DateTime?' to type 'System.Object'.
    ///     LINQ to Entities only supports casting Entity Data Model primitive types.
    /// </summary>
    /// <typeparam name="T">entity type</typeparam>
    /// <param name="query">query to apply sorting on.</param>
    /// <param name="expression">the member expression to apply</param>
    /// <param name="sortOrder">the sort order to apply</param>
    /// <returns>Query with sorting applied as IOrderedQueryable of type T</returns>
    public static IOrderedQueryable<T> ThenByMember<T>(
        this IQueryable<T> query, 
        Expression<Func<T, object>> expression, 
        SortOrder sortOrder)
    {
        return ((IOrderedQueryable<T>)query).ThenByMember(expression, sortOrder);
    }

    /// <summary>
    ///     Supports sorting of a given member as an expression when type is not known. It solves problem with LINQ to Entities unable to
    ///     cast different types as 'System.DateTime', 'System.DateTime?' to type 'System.Object'.
    ///     LINQ to Entities only supports casting Entity Data Model primitive types.
    /// </summary>
    /// <typeparam name="T">entity type</typeparam>
    /// <param name="query">query to apply sorting on.</param>
    /// <param name="expression">the member expression to apply</param>
    /// <param name="sortOrder">the sort order to apply</param>
    /// <returns>Query with sorting applied as IOrderedQueryable of type T</returns>
    public static IOrderedQueryable<T> ThenByMember<T>(
        this IOrderedQueryable<T> query, 
        Expression<Func<T, object>> expression, 
        SortOrder sortOrder)
    {
        var body = expression.Body as UnaryExpression;

        if (body != null)
        {
            var memberExpression = body.Operand as MemberExpression;

            if (memberExpression != null)
            {
                return
                    (IOrderedQueryable<T>)
                    query.Provider.CreateQuery(
                        Expression.Call(
                            typeof(Queryable), 
                            sortOrder == SortOrder.Ascending ? "ThenBy" : "ThenByDescending",
                            new[] { typeof(T), memberExpression.Type }, 
                            query.Expression,
                            Expression.Lambda(memberExpression, expression.Parameters)));
            }
        }

        return sortOrder == SortOrder.Ascending ? query.ThenBy(expression) : query.ThenByDescending(expression);
    }
0
répondu Peter Ragndahl 2013-11-11 10:29:25