简体   繁体   中英

How can I populate Extended Methods with EF5 and return IQueryable (without looping)

I'm new to Entity Framework. This is my first project using it.

I've created a new class that extends my "merchant" entity with new properties, which are mostly calculated totals. I could not figure out the best way to populate those new properties with data. I am using breeze.js to query an iQueryable endpoint so I need to keep this functionality intact.

I found the following solution, but it is very slow because I have to loop through every record. Despite the fact I have implemented paging in breeze the loop still hits every single record, though it only returns one page. I guess that is just how entity framework works. My question is how do I populate extended properties without looping?

Here is my code that works, but it is slow. Can I eliminate this loop and fill that extended property with a value?

    [HttpGet]
    public IQueryable<Merchant> MerchantList()
    {
        IQueryable<Merchant> items = _repo.Context.Merchants;

        foreach (var item in items)
        {

            // Total Stores
            item.TotalStores = myMethod(item.MerchantUID);

        }

        return items;

    }

By the looks of it you are looping through the whole tables worth of data on the off chance someone gets some of the rows. I'm not familiar with Breeze but I have worked with OData before so hopefully this solution will work for you.

I suggest that you stop exposing IQueryable<> and manage the query internally. This will enable you to add the additional information to only those records that you are returning to the client. Here's some rough code to point you in the right direction:

[HttpGet]
public PageResult<Merchant> MerchantList(
    ODataQueryOptions<Merchant> queryOptions)
{
    var t = new ODataValidationSettings() { MaxTop = 25 };
    var s = new ODataQuerySettings() { PageSize = 25 };
    queryOptions.Validate(t);
    IEnumerable<Merchant> results =
        (IEnumerable<Merchant>)queryOptions.ApplyTo(_repo.Context.Merchants, s);

    int skip = queryOptions.Skip == null ? 0 : queryOptions.Skip.Value;
    int take = queryOptions.Top == null ? 25 : queryOptions.Top.Value;
    long? count = Request.GetInlineCount();

    List<Merchant> pageOfResults = results.Skip(skip).Take(take).ToList();

    PageResult<Merchant> page =
        new PageResult<Merchant>(
            pageOfResults, 
            Request.GetNextPageLink(), 
            Request.GetInlineCount());

    foreach (var item in page)
    {

        // Total Stores
        item.TotalStores = myMethod(item.MerchantUID);

    }

    return page;
}

and the using s

using System.Web.Http;
using System.Web.Http.OData;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Query;

Thanks everyone for the direction. The suggestion that came the closest was the one about using oData. I researched eager loading, but that seems to apply to joining entities and these properties cannot be combined. I will probably do more research with "include" to determine if it can be used to load a single property, but I couldn't find any documentation on it used that way. Another thing that came close was projection, but EF doesn't allow projecting to the same type. I could have projected to a partial class, but then I would loose the MetaData that breeze needs. If not for breeze that would have been a really nice solution. In the end, while Breeze does support oData it has too many limitations (such as cannot be cached), so I wanted to stay with IQueryable.

This is what I came up with. It now only loops through the current page, but it still gets a count on the main query so Breeze can do paging on the client. This is all possible because Breeze allows me to send get parameters so I can offload the paging to the server. This took tons of research and it still isn't perfect, but it does load very fast so I have to move on for now.

    [HttpGet]
    [BreezeQueryable]
    public QueryResult MerchantList(int take, int skip)
    {

        IQueryable<Merchant> main = _repo.Context.Merchants.OrderBy(m => m.MerchantUID);

        IQueryable<Merchant> items = main.Skip(skip).Take(take);
        foreach (var item in items)
        {

            // Total LifetimeVal
            item.LifetimeVal= TotalLifetimeVal(item.MerchantUID);

            // Total Stores
            item.TotalStores = TotalStores(item.MerchantUID);

        }


        // return items;
        return new QueryResult
        {
            InlineCount = main.Count(),
            Results = items.ToList()
        };

    }

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