1

Sorry for my awful English.

I'm trying to extend my EF model (with LinqKit).

As long as I work with single entities expressions, I don't have any issue, but if I want to work with mixed expressions, I'm simply stuck.

For instance, a sort-of-post class like that...

partial class vit_post
{
//[...]
    public static Expression<Func<vit_post, bool>> HasMetas(IEnumerable<MetaFilter> metaArgs)
    {
        var resultsInner = PredicateBuilder.True<vit_post>();

        resultsInner.And(p=>p.vit_postmeta.Any(pm => (pm.hide == false)));

        foreach (var metaArg in metaArgs)
        {
            var mf = metaArg;

            resultsInner.And(p=>p.vit_postmeta.Any(pm => (pm.meta_key == mf.MetaKey && mf.Compare(pm.meta_value))));

            if (mf.ChildrenMetaFilters != null)
            {
                Expression<Func<vit_post, bool>> resultsInner2;
                switch (mf.LogicalOperator)
                {
                    case LogicalOperators.AND:
                        resultsInner2 = PredicateBuilder.True<vit_post>();
                        resultsInner = resultsInner2.And(HasMetas(mf.ChildrenMetaFilters));
                        break;
                    case LogicalOperators.OR:
                        resultsInner2 = PredicateBuilder.False<vit_post>();
                        resultsInner = resultsInner2.Or(HasMetas(mf.ChildrenMetaFilters));
                        break;
                }

            }
        }
        return resultsInner;
    }
//[...]
}

...extends an entity "vit_post" with an expression HasMetas.

Using this snippet, I get a subset of the entities, as expected:

    public SearchResults GetResults()
    {
            var query = dbc.vit_posts.AsExpandable();
    //[...]
            if (SearchArgs.Metas != null)
            {
                var postsbycontent = vit_post.HasMetas(SearchArgs.Metas);
                outer = Data.Utility.And(outer, postsbycontent);
            }
    //[...]
            query = query.Where(outer);

            var searchResults = new SearchResults
                        {
                            Items = searchResultsItems
                        };

            return searchResults;
    }

But if I try to move this this expression to the "vit_postmeta" entity like that:

partial class vit_postmeta
{
//[...]
        var resultsInner = PredicateBuilder.True<vit_postmeta>();

        resultsInner.And(pm => (pm.hide == false));

        foreach (var metaArg in metaArgs)
        {
            var mf = metaArg;

            resultsInner.And(pm => (pm.meta_key == mf.MetaKey && mf.Compare(pm.meta_value)));

            if (mf.ChildrenMetaFilters != null)
            {
                Expression<Func<vit_postmeta, bool>> resultsInner2;
                switch (mf.LogicalOperator)
                {
                    case LogicalOperators.AND:
                        resultsInner2 = PredicateBuilder.True<vit_postmeta>();
                        resultsInner = resultsInner2.And(HasMetas(mf.ChildrenMetaFilters));
                        break;
                    case LogicalOperators.OR:
                        resultsInner2 = PredicateBuilder.False<vit_postmeta>();
                        resultsInner = resultsInner2.Or(HasMetas(mf.ChildrenMetaFilters));
                        break;
                }

            }
        }
        return resultsInner;
//[...]
}

My idea was to keep the original method in vit_post and change it like that:

partial class vit_post
{
//[...]
    public static Expression<Func<vit_post, bool>> HasMetas(IEnumerable<MetaFilter> metaArgs)
    {
        var resultsInner = PredicateBuilder.True<vit_post>();
        resultsInner.And(p=>p.vit_postmeta.HasMetas(metaArgs));
        return resultsInner;
    }
//[...]
}

But I can't do that, as "'vit_postmeta' does not contain a definition for 'HasMetas' and no extension method 'HasMetas' accepting a first argument of type 'vit_postmeta' could be found".

I'm missing something, I know, but I can't find what.

UPDATE

I've found an alternate solution (and perhaps the proper one too), implementing all my expression in respective entity partial classes. For instance, vit_post had all expressions related to vit_posts's table, whilst vit_postmeta has all expressions related to vit_postmeta's table.

My Search class then has a private method for each entity. Something like:

            private IQueryable<vit_post> QueryPosts()
            {
                IQueryable<vit_post> queryPosts = VITContext.vit_posts.AsExpandable();
                Expression<Func<vit_post, bool>> outerPredicate = PredicateBuilder.True<vit_post>();

                if (!_searchArgs.IncludeExpiredPosts)
                {
                    Expression<Func<vit_post, bool>> postsByExpiration = vit_post.ExcludeExpired();
                    outerPredicate = Data.Utility.And(outerPredicate, postsByExpiration);
                }

                if (_searchArgs.LocaleCode != null)
                {
                    Expression<Func<vit_post, bool>> postsByLanguage = vit_post.HasLocaleCode(_searchArgs.LocaleCode);
                    outerPredicate = Data.Utility.And(outerPredicate, postsByLanguage);
                }
    [...]
    }

Then the GetResults() function calls all these methods and join them:

        internal string GetResults()
        {
                IQueryable<vit_post> queryPosts = QueryPosts();
if (_searchArgs.Metas != null)
                {
                    IEnumerable<vit_postmeta> queryMetas = QueryMetas();

                    queryPosts = from p in queryPosts
                                 join m in queryMetas
                                    on new {id = p.ID, loc = p.locale} equals new {id = m.post_id, loc = m.locale}
                                 select p;
                }

        }

This way I can make the query works, but I'm still not sure this is the right way to do it.

Andrea Sciamanna
  • 1,404
  • 1
  • 15
  • 30

1 Answers1

0

You did not see/answer my question yet, but if vit_postmeta and vit_post are in any way related by inheritance you may be able to do something like:

public static Expression<Func<T, bool>> HasMetas<T>(IEnumerable<MetaFilter> metaArgs)
    where T : vit_post
{
    ...
}

Assuming that vit_post is the super type.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • Thank you GertArnold. Sorry, I didn't get any notification about your previous question: perhaps I should have a look tomy account settings. About this possible solution, I'm a bit confused: how I'm suppose to implement this? Could you write an example (not necessarily based on my code)? – Andrea Sciamanna Sep 19 '11 at 07:18
  • @Andrea: I'm not sure if I can write a relevant example as I'm not familiar with Linqkit, but I was trying to make your expressions apply to a class and its derivatives, in stead of just one specific class. What I suggested would work with regular extension methods, but I'm afraid that Linqkit has it's own way of extending classes. With regular extension methods the method signature should be something like `HasMetas(this T post, IEnumerable<...`. – Gert Arnold Sep 19 '11 at 08:24
  • I see. In this case, I don't really need an extension method, as I'm already working within a partial class of the post's table. However, even though I'll keep in mind your suggestion for other purposes (really, thank you!), the actual issue is how to implement a multi-entity predicate expression (with LinqKit), in order to create custom queries based on user's input. – Andrea Sciamanna Sep 19 '11 at 09:17