Passing a predicate that was passed in as a parameter

948 views Asked by At

I'm having trouble with passing in a predicate to another function. This predicate would have been passed in as a parameter that is trying to call the second function. Below is a code snippet.

public IEnumerable<ViewModel> BuildModel<TPart, TRecord>(Expression<Func<TRecord, bool>> predicate) 
                where TPart : ContentPart<TRecord>
                where TRecord : ContentPartRecord
            {
                IEnumerable<ReportPart> items = GetList<ReportPart, ReportRecord>(predicate);

This issue is the predicate parameter, on the call to GetList() it keeps erroring, saying the call has some invalid arguments. The Get list call is:

 public IEnumerable<TPart> GetList<TPart, TRecord>(Expression<Func<TRecord, bool>> predicate)
            where TPart : ContentPart<TRecord>
            where TRecord : ContentPartRecord

I been trying changing the parameter a bunch of different ways trying to get this to work but I haven't had any success. Maybe I'm not understanding why the compiler thinks that 'predicate' is different than what GetList() is expecting.

EDIT: more information

ReportPart : ContentPart<ReportRecord>

ReportRecord : ContentPartRecord

ContentPart and ContentPartRecord are both base classes

Caller to BuildModels

List<ReportViewModel> model = _service.BuildReports<ReportPart, ReportRecord>(x => x.Id == 1).ToList();

BuildModels

public IEnumerable<ReportViewModel> BuildReports<TPart, TRecord>(System.Linq.Expressions.Expression<Func<TRecord, bool>> predicate)
            where TPart : ContentPart<TRecord>
            where TRecord : ContentPartRecord
{
            List<ReportViewModel> model = new List<ReportViewModel>();
            IEnumerable<ReportPart> reportParts = GetList<ReportPart, ReportRecord>(predicate);
            //do some stuff with reportParts
            return model;
    }
}

GetList

 public IEnumerable<TPart> GetList<TPart, TRecord>(System.Linq.Expressions.Expression<Func<TRecord, bool>> filter)
            where TPart : ContentPart<TRecord>
            where TRecord : ContentPartRecord
        {
            return filter == null ?
                Services.ContentManager.Query<TPart, TRecord>().List() :
                Services.ContentManager.Query<TPart, TRecord>().Where(filter).List();
        }
1

There are 1 answers

1
Peter Duniho On BEST ANSWER

Without a good, minimal, complete code example, it's impossible to know for sure what the best fix for your problem is, assuming one exists at all.

That said, variance problems generally come in two flavors: 1) what you're doing is truly wrong and the compiler is saving you, and 2) what you're doing is not provably correct, so you have to promise the compiler you know what you're doing.

If you're in the first category, then all is lost. You can't get this to work.

But if you're in the second category, you may be able to get your call to work by wrapping the original predicate in a new one that is compatible with the called method's requirements:

IEnumerable<ReportPart> items =
    GetList<ReportPart, ReportRecord>(r => predicate((TRecord)r));


That said, while it's possible that there's some important reason for you writing the code this way, given the little bit of code you've shown so far, it's not really clear why you're trying to take the generic predicate and force it into the specific type.

Depending on what's really going on in the rest of the code, a pair of generic methods like this would work better where you either 1) go fully generic (i.e. don't force the type as ReportRecord in the call to GetList()), or 2) you don't bother with the generic types at all (i.e. leave out TPart and TRecord from the BuildModel() method).

Example of 1):

public IEnumerable<ViewModel> BuildModel<TPart, TRecord>(
    Expression<Func<TRecord, bool>> predicate) 
        where TPart : ContentPart<TRecord>
        where TRecord : ContentPartRecord
{
    IEnumerable<TPart> items = GetList<TPart, TRecord>(predicate);
}

Example of 2):

public IEnumerable<ViewModel> BuildModel(
    Expression<Func<ReportRecord, bool>> predicate)
{
    IEnumerable<ReportPart> items = GetList<ReportPart, ReportRecord>(predicate);
}

The mixing and matching, even if you can get it to work correctly, is often a sign that there's a more fundamental problem in the architecture, where generics are either being used where they shouldn't be, or are not being taken advantage as well as they should be.


If the above does not get you back on track, you really should improve the question by providing a minimal, complete code example.