3

次の db クエリ ビルダーをジェネリックにする方法はありますか?

private IQueryable<Foo> ByName(IQueryable<Foo> dbQuery, Query query)
{
    string[] searchTerms = query.Data.Replace(" ","").ToLower().Split(',');

    if (query.Exclude)
    {
        return dbQuery.Where(x => searchTerms.All(
            y => y != x.Name.Replace(" ", "").ToLower()));
    }

    return dbQuery.Where(x => searchTerms.Any(
        y => y == x.Name.Replace(" ", "").ToLower()));
}

Foo の多くの異なるプロパティに対して同じ機能を持っています。ByCounty、ByTown、ByStreet など。

次のように前にlinqを返す関数をいくつか書きました

public Expression<Func<Foo, bool>> FoosAreWithinDistanceFromGeocode(
    double distance, Geocode geocode)
{
    double distanceSquare = distance * distance;
    return foo => ( SqlFunctions.Square((double)(
        foo.Address.Geocode.Easting - geocode.Easting)) +
        SqlFunctions.Square((double)(fooAddress.Geocode.Northing - 
        geocode.Northing)) ) <= distanceSquare;
}

しかし、Linq-to-SQL がジェネリックを使用できないかどうか、またはプロパティをジェネリックとして渡すことができるかどうか、およびそのようなことを見つけることができないようです。


編集:これは、単一の検索用語に対して一般的に機能しています。

Where [query.Data == "Foo1"]

return dbQuery.Where(SearchMatch("Name", query.Data));

public Expression<Func<Foo, bool>> SearchMatch(string propertyName, string searchTerm)
{
    var foo = Expression.Parameter(typeof(Foo), "foo");
    var prop = Expression.Property(foo, propertyName);
    var search = Expression.Constant(searchTerm);
    var equal = Expression.Equal(prop, search);

    return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}

文字列の配列で機能させる方法を知っている人はいますか?

4

2 に答える 2

6

次のように、アクセスするプロパティを公開するインターフェイスを定義する必要があります。

public interface IHaveName
{
    string Name { get; }
}

次に、クラスでインターフェースを実装します。

public class Foo : IHaveName

DBML ファイルから生成されたクラスを使用している場合、これらのクラスはpartialキーワードでマークされているため、インターフェースの実装は、新しいファイルを作成して挿入するのと同じくらい簡単です。

public partial class Foo : IHaveName

プロパティは、.dbml ファイルから生成された他の.cs ファイルで既に public として宣言されているため、インターフェイスは暗黙的に実装されます。

最後に、メソッドを書き直してByName、インターフェイスを実装するという制約付きのジェネリック型パラメーターを取得しますIHaveName

private IQueryable<T> ByName<T>(IQueryable<T> dbQuery, Query query)
    where T : IHaveName
{
    // Everything else is the same.

他のプロパティ (およびそれらを使用するメソッド) については、必要に応じて、それらを 1 つのインターフェイスにまとめたり、分離したりできます。


編集に基づいて、式を動的に作成する場合は、コンパイル時の安全性をあきらめる必要はありません。

public Expression<Func<Foo, bool>> SearchMatch(
    Expression<Func<Foo, string>> property, string searchTerm)
{
    var foo = Expression.Parameter(typeof(Foo), "foo");
    // Get the property info from the property expression.
    var prop = Expression.Property(foo, 
        (property.Body as MemberExpression).Member as PropertyInfo);
    var search = Expression.Constant(searchTerm);
    var equal = Expression.Equal(prop, search);

    return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}

次に、次のように呼び出します。

var expression = SearchMatch(f => f.Name, "searchTerm");

これにより、渡すプロパティが にSearchMatch実際に存在することが保証されFooます。これを他のスカラー プロパティ タイプに対してジェネリックにしたい場合は、次のようにします。

public Expression<Func<Foo, bool>> SearchMatch<T>(
    Expression<Func<Foo, T>> property, T searchTerm)
{
    var foo = Expression.Parameter(typeof(Foo), "foo");
    // Get the property info from the property expression.
    var prop = Expression.Property(foo, 
        (property.Body as MemberExpression).Member as PropertyInfo);
    var search = Expression.Constant(searchTerm);
    var equal = Expression.Equal(prop, search);

    return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
于 2012-11-01T15:42:43.020 に答える
-1

あなたが反射を達成しようとしていることを理解していれば、あなたを助けるかもしれません。せめて上手に弾けば。これは単純化された実際の例です

internal class Program
{
    private class Data
    {
        public string Name { get; set; }
        public string Address { get; set; }

        public override string ToString()
        {
            return String.Format("My name is {0} and I'm living at {1}", Name, Address);
        }
    }

    static Expression<Func<Data,bool>> BuildExpression(PropertyInfo prop, IQueryable<string> restrict)
    {
        return (data) => !restrict.Any(elem => elem == prop.GetValue(data, null));
    }

    static IQueryable<Data> FilterData(IQueryable<Data> input, Expression<Func<Data, bool>> filter)
    {
        return input.Where(filter);
    }

    public static void Main (string[] args)
    {
        List<Data> list = new List<Data>()
                               {
                                   new Data {Name = "John", Address = "1st Street"},
                                   new Data {Name = "Mary",Address = "2nd Street"},
                                   new Data {Name = "Carl", Address = "3rd Street"}
                               };

        var filterByNameExpression = BuildExpression(typeof (Data).GetProperty("Name"),
                                                     (new List<string> {"John", "Carl"}).AsQueryable());

        var filterByAddressExpression = BuildExpression(typeof(Data).GetProperty("Address"),
                                                     (new List<string> { "2nd Street"}).AsQueryable());

        IQueryable<Data> filetedByName = FilterData(list.AsQueryable(), filterByNameExpression);
        IQueryable<Data> filetedByAddress = FilterData(list.AsQueryable(), filterByAddressExpression);

        Console.WriteLine("Filtered by name");
        foreach (var d in filetedByName)
        {
            Console.WriteLine(d);
        }

        Console.WriteLine("Filtered by address");
        foreach (var d in filetedByAddress)
        {
            Console.WriteLine(d);
        }

        Console.ReadLine();
    }

ただし、LINQ-to-SQL では機能しないと確信しています。これを回避する 1 つの方法は、そのIQueryableようなフィルタリング メソッドに渡す前に (たとえば、ToListそれらを呼び出して) を具体化することです。

于 2012-11-01T15:51:26.357 に答える