More in this section

Forums / Bugs & Issues / Fluent API limitations

Fluent API limitations

18 posts, 1 answered
  1. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    08 Feb 2012
    Link to this post
    Hi,

    I keep hitting fluent API limitations, as there are many types of queries it doesn't seem to support. I just got "Current LINQ provider does not support 'Max' method.". In the past, I also had problems when trying to match strings in various ways. (Edit: no ThenBy method following OrderBy, either)

    Is there a PITS numbers for such issues?

    Thanks.
  2. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    10 Feb 2012
    Link to this post
    Hi Thomas,

     Most of the limitations to the queries are due to the OpenAccess LINQ parser, not the Fluent API. However, if you have any features that are in particular connected to the Fluent API (like public methods or properties that are not implemented or new feature that you want to see) and you do not find them in PITS, you can always send us feature requests and we will log them into the system.

    Kind regards,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  3. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    27 Feb 2012
    Link to this post
    Hi,

    I just hit one more limitation: {"Execution of 'System.Linq.Enumerable:Intersect(IEnumerable`1,IEnumerable`1)' on the database server side currently not implemented."}

    These problems force us to fetch collections early, before filtering them, which kills our site performances. As we currently face critical performance issues that we have to fix ASAP, we're going to have to complicate our code to speed it up, due to such issues. I'm currently thinking of implementing our own cache, can't think of anything else... It's not ideal, though.

    Are we doing it wrong? If you can think of a way to work around these problems, please advise! Thanks.

    For the record, we are currently in a situation which may end up making us drop Sitefinity, due to pressure from higher-ups in the company to get something working.
  4. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    29 Feb 2012
    Link to this post
    Hi Thomas,

     I am really sorry to hear this. I am sure we can help you solve the issues, but can you tell me what is the specific case that you have? Exactly what kind of filtering are you trying to perform. Can you give me a piece of code, so that I can see if I can offer you an optimization option for it? Thanks in advance.

    Kind regards,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  5. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    29 Feb 2012
    Link to this post
    Hi Svetoslav,

    Thanks for your answer.

    Let's focus on a specific example: a custom widget that filters news items based on 3 classifications (Tags, Categories, and a custom classification).
    We could move some simple operations to the DB side (using the NewsPluralFacade<BaseFacade> class, ie: before calling the Get() method), such as PublicationDate > something, and OrderBy operations, so they execute fast (much faster than if we do it after calling Get()). However, we couldn't make the IEnumerable<Guid>.Intersect() method work this way, so we had to do that after calling Get().

    Relevant pieces of code:

    IEnumerable<NewsItem> newsItems = App.WorkWith().NewsItems().Publihed()
            .Where(n => n.PublicationDate >= this.startDate)
            .OrderByDescending(i => i.PublicationDate).Get();
      
    if (this.categoryTaxa != null)
    {
        newsItems = TaxonomyHelper.Instance.FilterItems<NewsItem>(newsItems, this.categoryTaxa, CategoryHelper.FieldName);
    }
      
    if (this.tagTaxa != null)
    {
        newsItems = TaxonomyHelper.Instance.FilterItems<NewsItem>(newsItems, this.tagTaxa);
    }
      
    if (this.newsTypeTaxa != null)
    {
        newsItems = TaxonomyHelper.Instance.FilterItems<NewsItem>(newsItems, this.newsTypeTaxa);
    }
      
    if (this.Limit >= 0)
    {
        newsItems = newsItems.Take(this.Limit);
    }
      
    // ... in TaxonomyHelper:
      
    public IEnumerable<T> FilterItems<T>(IEnumerable<T> items, IEnumerable<Taxon> taxa) where T : Content
    {
        if (!taxa.Any())
        {
            return new T[0];
        }
      
        string taxonomyName = taxa.First().Taxonomy.Name;
        return this.FilterItems<T>(items, taxa, taxonomyName);
    }
      
    public IEnumerable<T> FilterItems<T>(IEnumerable<T> items, IEnumerable<Taxon> taxa, string fieldName) where T : Content
    {
        var ids = from t in taxa select t.Id;
        return items.Where(d => d.GetValue<TrackedList<Guid>>(fieldName).Intersect(ids).Any());
    }

    Can you think of ways to speed this up?

    Thanks again.
  6. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    01 Mar 2012
    Link to this post
    Hi Thomas,

     Can you give me a little more information on what is the end purpose of this code. If it is filtering items by taxonomies, here's a code that I can offer and that works with both fluent and native API (also if something isn't working with fluent API, be sure to try it with the Native API, as well). The example is for news:

    TaxonomyManager manager = TaxonomyManager.GetManager();
                var name = "test";
                var taxa = manager.GetTaxa<HierarchicalTaxon>();
                var taxon = taxa.Where(t => t.Title == name).SingleOrDefault().Id;
                NewsManager mng = NewsManager.GetManager();
                var newsNative = mng.GetNewsItems().Where(i => i.GetValue<TrackedList<Guid>>("Category").Contains(taxon));
                var newsFluent = App.WorkWith().NewsItems().Where(i => i.GetValue<TrackedList<Guid>>("Category").Contains(taxon));
     
    Also, there is no problem calling the Get() method. It returns an IQueryable collection, so it doesn't matter if the Where clause is before or after the Get() method - it won't execute until after the IQueryable collection has been cast to an IEnumerable.  

    Regards,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  7. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    02 Mar 2012
    Link to this post
    Hi Svetolsav,

    Yes, as I said, I want to filter items by taxonomies / classifications. Like in your example, except I handle 3 collections of taxa, and not just one taxon. Could you post the equivalent code for handling a collection of taxa, rather than a single taxon? Thanks.

    Also, there is no problem calling the Get() method. It returns an IQueryable collection, so it doesn't matter if the Where clause is before or after the Get() method - it won't execute until after the IQueryable collection has been cast to an IEnumerable.

    Yes, that's what I thought, but as I benchmarked performances, I noticed that on one or two occasions, it wasn't the case. Maybe I hit a weird case where IQueryable items were implicitly fetched, or something.
  8. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    07 Mar 2012
    Link to this post
    Hi Thomas,

     Multiple collections of taxons can be used for filtering by just adding more Where clauses to the IQueryable (this won't increase the load). As long as you keep the collection to be an IQueryable, adding more filtering clauses wouldn't cause any problems.
     On the IQueryable cast to IEnumerable -  If you find a pattern for such a behaviour (and it is a bug coming from Sitefinity), please make sure to get back to me with steps to reproduce. 

    Regards,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  9. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    07 Mar 2012
    Link to this post
    Hi Svetoslav,

    If I'm not mistaken, more Where clauses wouldn't do what I want, because I want an "or"-based query, not "and"-based. The more taxon filters I get as parameter, the more results I want to return, not the other way around. That is to say that if I have a collection of 3 taxons as parameter, I want to build a query like "select news where news.taxa contains taxon1 or news.taxa contains taxon2 or news.taxa contains taxon3". That's why in the code I showed before, I used the Intersect method.

    If you have a better (faster) way to do that kind of filtering than using the Intersect method like I did, I'm interested.
  10. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    12 Mar 2012
    Link to this post
    Hi Thomas,

     What you can do is create a filter expression with a StringBuilder (for each guid in your collection of guids you should append a new "OR {ID} " to the expression) and in the end pass this expression to a Where clause. In order to have a Where clause with a string expression, you need to use our Dynamic LINQ extensions, that can be accessed by using the Telerik.Sitefinity.Data.Linq.Dynamic namespace. 

    Greetings,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  11. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    13 Mar 2012
    Link to this post
    Hi Svetoslav,

    Thanks for the info, I didn't know about this possibility. Could you give me a full concrete example? I can't get it to work.

    This is my method:

    public NewsPluralFacade<BaseFacade> FilterItems(NewsPluralFacade<BaseFacade> items, IEnumerable<Taxon> taxa, string fieldName)
    {
        var ids = from t in taxa select t.Id;
        StringBuilder sb = new StringBuilder();
        // ??
        string query = sb.ToString();
        return items.Where(query);
    }


    PS: rather than accumulating OR conditions, maybe there is support for the IN keyword? Like "{ID} IN (id1, id2, id3...)"?

    Thanks.
  12. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    16 Mar 2012
    Link to this post
    Hello Thomas,

     What you need to do is create a loop and append the IDs one by one to the String builder, like that:

    for (int i = 0; i < taxa.Count; i++)
               {
                   if (i == 0)
                       sb.AppendFormat("Id = {0}", taxa[i].ToString());
                   else
                       sb.AppendFormat(" OR Id = {0}", taxa[i].ToString());
               }

    All the best,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  13. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    16 Mar 2012
    Link to this post
    Hi Svetoslav,

    Thanks, but it still doesn't work.

    First, doing taxa[i].ToString() will give me its type: "Telerik.Sitefinity.Taxonomies.Model.HierarchicalTaxon"
    ... which doesn't look something I'd like to insert in a query.

    Also, do I not need to specify somewhere that I'd like to filter based on classifications, or even a certain classification? How can the system guess that when I say "Id", I mean a certain classification Id?

    Here's what my code currently looks like:

    public NewsPluralFacade<BaseFacade> FilterItems(NewsPluralFacade<BaseFacade> items, IEnumerable<Taxon> taxa)
    {
        if (!taxa.Any())
        {
            return items.Where(i => false);
        }
     
        string query = "Id = " + string.Join(" OR Id = ", taxa.Select(t => t.Id.ToString()));
     
        var test = items.Where(query).Get().ToArray(); // Returns an empty array
     
        return items.Where(query);
    }


    This doesn't throw an exception, but returns an empty collection.
  14. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    21 Mar 2012
    Link to this post
    Hello Thomas,

     Actually I forgot to add that you need to add Property.Contains((id)) to the filter expression for each of the IDs, where Property is the name of the relevant classification field.

    Regards,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  15. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    21 Mar 2012
    Link to this post
    Hi Svetoslav,

    Sorry, I still don't get it. I don't know where or how I'm supposed to handle the property (maybe in the query string, replacing the equal sign?). Also, I could be wrong, but as far as I know, the data I'm interested in is not available as a property. That is to say that taxon objects do not have a property that specifies which taxonomies they belong to, which is why I do d.GetValue<TrackedList<Guid>>(fieldName) in my original code.

    Could you please post a full, working code sample? That'd help a lot, and clear all the confusion. Thanks.
  16. Svetoslav Petsov
    Svetoslav Petsov avatar
    456 posts
    Registered:
    24 Sep 2012
    23 Mar 2012
    Link to this post
    Hi Thomas,

     Here's an example of the expression that you need to create with the string builder:

    NewsManager mng = NewsManager.GetManager();
                string expression = "Category.Contains((9A187790-BE97-414F-BE53-03739E8D191F)) OR Category.Contains((3D387012-5BA2-41A8-8732-D870F6BFB12F))";
                var result = mng.GetNewsItems().Where(expression);

    Regards,
    Svetoslav Petsov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
    Answered
  17. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    26 Mar 2012
    Link to this post
    Hi Svetoslav,

    At last, it works! I was just missing the double brackets.
    This took a while, but was worth investigating, because performances are significantly better than with the Intersect method. Quick benchmarking shows it's now 2 to 5 times faster, depending on cases.

    Thanks for your help.
  18. Thomas
    Thomas avatar
    223 posts
    Registered:
    19 Jan 2011
    12 Apr 2012
    Link to this post
    PS: the Max() method now works with Sitefinity 5.0, it seems.
18 posts, 1 answered