Order By()、Where() 等在具有泛型类型的函数中使用

Ordeby(), Where(), etc. used in a function with generic types

我目前正在摆弄通用存储库。我的测试应用程序是在 C# 中。我做了一个函数,可以打印出特定存储库中的所有项目。存储库是输入参数。整个块位于此 post 的底部,目前函数本身如下所示:

static void PrintCollection<T, TKey>(IRepository<T, TKey> repo)
{
    // Error since compiler does not recognizes p.LastName
    //foreach (Person p in repo.GetItems().OrderBy(p => p.LastName)) Console.WriteLine(Convert.ToString(p));
    foreach (T p in repo.GetItems())
        Console.WriteLine(Convert.ToString(p));
}

它工作正常,但它缺少某些东西,例如 class 中的特定 属性 排序,例如 Person 中的 LastNameProductName Product。我想知道是否可以向 PrintCollection<T, TKey>(...) 发送一个额外的参数并测试特定字段是否存在,然后按它排序。例如:

static void PrintCollection<T, TKey, TOrder>(IRepository<T, TKey> repo, TOrder o = default(TOrder))
{
    if (o == default(TOrder))
        foreach (T p in repo.GetItems()) Console.WriteLine(Convert.ToString(p));
    else
        // Order by o
}

目前,我不知道应该使用什么来测试字段和属性是否存在于 class 中。然后,如何在 LINQ 和 Lambdsa 表达式中使用它们,例如排序、排序、过滤集合。

谢谢


下面是我的代码片段。我删掉了不重要的部分。

主程序

class Program
{
    static void Main(string[] args)
    {
        PeopleRepository repPeople = new PeopleRepository();
        ProductsRepository repProduct = new ProductsRepository();

        PrintCollection(repPeople);
        PrintCollection(repProduct);
    }

    static void PrintCollection<T, TKey>(IRepository<T, TKey> repo)
    {
        // Error since compiler does not recognizes p.LastName
        //foreach (Person p in repo.GetItems().OrderBy(p => p.LastName)) Console.WriteLine(Convert.ToString(p));
        foreach (T p in repo.GetItems()) Console.WriteLine(Convert.ToString(p));
    }
}

界面IRepository

interface IRepository<T, TKey>
{
    IEnumerable<T> GetItems();
    T GetItem(TKey key);
    // ...
}

人的存储库

class PeopleRepository : IRepository<Person, string>
{
    private static List<Person> _proxy;

    public PeopleRepository(List<Person> people = null)
    {
        if(people == null) _proxy = Person.GetAllPeople();
        else _proxy = people;
    }

    public IEnumerable<Person> GetItems()
    {
        return _proxy;
    }

    public Person GetItem(string key)
    {
        return _proxy.SingleOrDefault(p => p.ID == key);
    }
}

产品存储库

class ProductsRepository : IRepository<Product, int>
{
    private static List<Product> _proxy;

    public ProductsRepository(List<Product> products = null)
    {
        if (products == null) _proxy = Product.GetAllProducts();
        else _proxy = products;
    }

    public IEnumerable<Product> GetItems()
    {
        return _proxy;
    }

    public Product GetItem(int key)
    {
        return _proxy.SingleOrDefault(p => p.ID == key);
    }
}

是的,您可以,使用委托来按子句选择顺序:

static void PrintCollection<T, TKey, TOrder>(IRepository<T, TKey> repo, 
       Func<T,TOrder> orderBy)
{
    var items = repo.GetItems();
    if (orderBy != null)
        items = items.OrderBy(orderBy);
    foreach (T p in items)
        Console.WriteLine(Convert.ToString(p));
}

你可以这样称呼它:

PrintCollection(repo, x=>x.Name);

对于Where你需要Func<T,bool>类型的参数,过程同上。

注意: 如果您使用像 EF 这样的 ORM,ORM 需要整个表达式树才能为查询构建 SQL 语句。当我们传递委托时,排序或过滤不会转换为 SQL。 ORM 加载数据然后对其应用排序或过滤。

如果您想确保它们转换为 SQL,return 类型必须是实现 IQueryable 的类型并且函数的参数应更改为 Expression<Func<T,TOrder>> 对于 orderByExpression<Func<T,bool>> 对于 while.

建议

将此方法定义为 IEnumerable 的扩展:

public static class IEnumerableExtenstions
{
    static void PrintCollection<T>(this IEnumerable<T> collection)
    {
        foreach (T p in collection)
            Console.WriteLine(Convert.ToString(p));
    }
}

然后你可以这样称呼它:

repo.GetItems().Where(x=>x.Age>15).OrderBy(x=>x.Name).PrintCollection();

这比在函数中为每个功能定义参数更加灵活和自然。