Как я могу заставить этот запрос работать в LINQ to Entities? - PullRequest
0 голосов
/ 02 февраля 2012

Мне нужно следующий код:

private static bool DoesColValueExist<T>(IQueryable dataToSearchIn, string colName, string colValue)
{
    int noOfClients = 1;
    Type type = typeof(T);
    if (colValue != "" && colName != "")
    {
        var property = type.GetProperty(colName);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        Expression left = Expression.Call(propertyAccess, typeof(object).GetMethod("ToString", System.Type.EmptyTypes));
        left = Expression.Call(left, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
        Expression right = Expression.Constant(colValue.ToLower(), typeof(string));
        MethodInfo method = typeof(string).GetMethod("Equals", new[] { typeof(string) });
        Expression searchExpression = Expression.Call(left, method, right);


        MethodCallExpression whereCallExpression = Expression.Call(
            typeof(Queryable),
            "Where",
            new Type[] { type },
            dataToSearchIn.Expression,
            Expression.Lambda<Func<T, bool>>(searchExpression, new ParameterExpression[] { parameter }));
        var searchedData = dataToSearchIn.Provider.CreateQuery(whereCallExpression);
        noOfClients = searchedData.Cast<T>().Count();

        if (noOfClients == 0)
            return false;
        else
            return true;
    }
    return true;
}

Он работает с LINQ to SQL, но с LINQ to Entities я получаю сообщение об ошибке:

LINQ to Entities не распознает метод метода System.String ToString (), и этот метод нельзя преобразовать в выражение хранилища.

Ответы [ 2 ]

1 голос
/ 04 февраля 2012

Linq to Entities не поддерживает метод .ToString ().Я также не уверен, если это хорошая идея использовать сравнение строк для типов, которые не являются строками.Но не все потеряно.Я придумал следующее решение:

public partial class MyEntity
{
    public int ID { get; set; }
    public int Type { get; set; }
    public string X { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<MyEntity> Entities { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());

        using (var ctx = new MyContext())
        {
            if (!ctx.Entities.Any())
            {
                ctx.Entities.Add(new MyEntity() { ID = 1, Type = 2, X = "ABC" });
                ctx.SaveChanges();
            }

            Console.WriteLine(DoesColValueExist(ctx.Entities, e => e.X, "aBc"));
            Console.WriteLine(DoesColValueExist(ctx.Entities, e => e.X, "aBcD"));
            Console.WriteLine(DoesColValueExist(ctx.Entities, e => e.Type, 2));
            Console.WriteLine(DoesColValueExist(ctx.Entities, e => e.Type, 5));

        }
    }

    private static bool DoesColValueExist<TEntity, TProperty>(IQueryable<TEntity> dataToSearchIn, Expression<Func<TEntity, TProperty>> property, TProperty colValue)
    {

        var memberExpression = property.Body as MemberExpression;
        if (memberExpression == null || !(memberExpression.Member is PropertyInfo))
        {
            throw new ArgumentException("Property expected", "property");
        }

        Expression left = property.Body;
        Expression right = Expression.Constant(colValue, typeof(TProperty));
        if (typeof(TProperty) == typeof(string))
        {
            MethodInfo toLower = typeof(string).GetMethod("ToLower", new Type[0]);
            left = Expression.Call(left, toLower);
            right = Expression.Call(right, toLower);
        }

        Expression searchExpression = Expression.Equal(left, right);
        var lambda = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(left, right), new ParameterExpression[] { property.Parameters.Single() });

        return dataToSearchIn.Where(lambda).Any();                
    }
}

Приятно то, что оно более безопасно для типов, чем решение на основе строк - значение параметра должно совпадать со значением свойства,Свойство, в свою очередь, должно быть членом сущности, которая является универсальным типом IQueryable'1, переданного в качестве первого параметра.Еще одна полезная вещь: при кодировании этого метода intellisense покажет вам элемент сущности, когда вы начнете вводить лямбда-выражение для второго параметра.В самом методе я добавил исключение для строкового типа, когда я вызываю .ToLower () как для значения свойства, так и для запрошенного значения, чтобы сделать сравнение без учета регистра.Для нестроковых типов значения сравниваются «как есть», то есть без каких-либо изменений.Приведенный выше пример завершен - вы можете скопировать и вставить его в проект консольного приложения (хотя вам нужно сослаться на EntityFramework.dll).Надеюсь это поможет.

0 голосов
/ 04 февраля 2012

Попробуйте это:

private static bool DoesColValueExist<T>(IQueryable dataToSearchIn, string colName, string colValue)
{
    int noOfClients = 1;
    Type type = typeof(T);
    if (colValue != "" && colName != "")
    {
        var property = type.GetProperty(colName);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        Expression left = property.PropertyType == typeof(string) ? propertyAccess : Expression.Call(propertyAccess, typeof(object).GetMethod("ToString", System.Type.EmptyTypes));
        left = Expression.Call(left, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
        Expression right = Expression.Constant(colValue.ToLower(), typeof(string));
        MethodInfo method = typeof(string).GetMethod("Equals", new[] { typeof(string) });
        Expression searchExpression = Expression.Call(left, method, right);


        MethodCallExpression whereCallExpression = Expression.Call(
            typeof(Queryable),
            "Where",
            new Type[] { type },
            dataToSearchIn.Expression,
            Expression.Lambda<Func<T, bool>>(searchExpression, new ParameterExpression[] { parameter }));
        var searchedData = dataToSearchIn.Provider.CreateQuery(whereCallExpression);
        noOfClients = searchedData.Cast<T>().Count();

        if (noOfClients == 0)
            return false;
        else
            return true;
    }
    return true;
}

Обычно, если свойство является строкой, оно не вызывает метод ToString().

Надеюсь, это поможет.

...