简体   繁体   中英

MVC4 pass lambda from controller to repository

I have a controller that calls a method in my repository that runs a linq to entities query to get a list of products for my application. I'd like to add the ability for a user to filter the results (something like standard price high to low, low to high, etc etc).

So I'm thinking I need to dynamically populate my orderby in the repository, but am having trouble with this. I'm shaky on generics in c#, so please bear with me.

Controller

model.Products = ProdRepo.GetProducts ((int)CategoryId, 0, 8);

Model

    //get a list of the products
    public List<ProductModel> GetProducts (int CategoryId, int Start, int End) 
    {
        List<ProductModel> Products = new List<ProductModel>();
        var products = (from p in db.products select p).AsEnumerable().OrderByWithDirection(x=> x.date_added, true).Where( x => x.category_id == CategoryId).Where((row, index) => index >= Start && index < End).ToList();
            if (products.Count > 0)
            {
                foreach (var item in products)
                {
                    ProductModel model = new ProductModel();
                    model.CategoryId = item.category_id;
                    model.Description = item.description;
                    model.Id = item.id;
                    model.Product = item.product1;
                    model.DateAdded = item.date_added;
                    model.Image = item.image;
                    model.Price = item.price;

                    Products.Add(model);
                }
            }
      }

So I'm thinking I need to pass a Func<TSource, TKey> from my controller, but I having trouble piecing together how to accomplish that. Any help would be greatly appreciated.

You can do this by passing in a Func<IQueryable<T>, IOrderedQueryable<T>> which in this case would be

 public List<ProductModel> GetProducts (int CategoryId, int Start, int End, Func<IQueryable<ProductModel>, IOrderedQueryable<ProductModel>> order? = null)
 {
  //code shown in question

  if( order != null )
  {
   Products = order(Products).ToList();
  }
 }

And you could call it like this:

model.Products = ProdRepo.GetProducts ((int)CategoryId, 0, 8, order: q => q.OrderBy(prod => prod.SomeField));

I'm assuming the struggle is coming in because the ordering element is not always going to be the same type. Rather than overcomplicating a fairly simple choice by passing around a genericized generic, why not just pass in the option and take it into account while building the linq query?

In the code at my workplace we'd probably achieve this with an enum, something like:

public enum OrderByValue
{
    DateAdded,
    Price,
    OtherChoice
}

Then, your query would just have a decision to make in the middle:

var products = (from p in db.products select p).AsEnumerable();
switch(orderByValue)
{
    case OrderByValue.DateAdded:
    products = products.OrderByWithDirection(x=> x.date_added, true);
    break;

    case OtherStuff:
    ...
}
products = products.Where( x => x.category_id == CategoryId)
    .Where((row, index) => index >= Start && index < End)
    .ToList();

If it is in fact just a binary choice you could also do the same without the enum and the switch, and just pass in a bool.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM