简体   繁体   中英

Access property of a generic type inside a function

I have a generic function GetDocuments<T> that's querying the CosmosDB API. The generic is constrained by a custom IDocument interface. At the moment, I'm passing an enum as an argument to this function that determines the type of the document -- however, my interface has the document type as a property, so it seems like I should be able to access that somehow instead of passing another arg.

Because my argument is in an Expression , I'm not sure how to access that value (I'm not sure if using the API to access expression params is the right approach). If I had an IDocument as an argument, it seems pretty straightforward to access it.

Given this code, how can I access the DocumentType without passing it to GetDocuments<T> ?

Function definition:

public IEnumerable<T> GetDocuments<T>(Expression<Func<T, bool>> predicate, Enumerations.DocumentType type) where T : IDocument
{
    var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
        .Where(predicate)
        .Where(s => s.DocumentType == type)
        .ToList();

    return results;
}

Interface definition:

public interface IDocument
{
    [JsonProperty(PropertyName = "id")]
    string Id { get; set; }
    [JsonProperty(PropertyName = "documentType")]
    Enumerations.DocumentType DocumentType { get; }
}

Function call:

var messages = mailboxRepository.GetDocuments<MailboxMessageTemplate>(
            s => s.UserId == user.ID,
            Enumerations.DocumentType.MessageTemplate);

You can do that by pre-creating your expression and just adding it to your query.

Here is the expression that would do the trick.

internal static Expression<Func<T, bool>> TypeSpecificExpression<T>() where T : class
{
    var parameter = Expression.Parameter(typeof(IDocument));
    var member = Expression.Property(parameter, nameof(IDocument.Enumerations.DocumentType));
    var contant = Expression.Constant(nameof(T));
    var body = Expression.Equal(member, contant);
    var extra = Expression.Lambda<Func<T, bool>>(body, parameter);
    return extra;
}

You can then simple change your method to be:

public IEnumerable<T> GetDocuments<T>(Expression<Func<T, bool>> predicate) where T : IDocument
{
    var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
        .Where(predicate && TypeSpecificExpression())
        .ToList();

    return results;
}

Obviously I don't have access to the Enumerations.DocumentType enum so you might need to do some tweeting on the value you are setting here: var contant = Expression.Constant(nameof(T));

On a side note, you should not be calling .ToList() like that on CreateDocumentQuery . You are synchornizing a query that can be a serious performance hit. You should be using the .AsDocumentQuery() method to get the query and then call query.ExecuteNextAsync when query.HasMoreResults .

On a second side note, it looks like you are trying to build something that the library Cosmonaut already does, including the feature you just asked a question for (you can find that method here ). It's worth taking a look.

Disclaimer: I made Cosmonaut

So you are looking for a way to convert an Expression<Func<T, bool>> to a Func<T, bool> ?

You can call Compile .

Compiles the lambda expression described by the expression tree into executable code and produces a delegate that represents the lambda expression.

var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
    .Where(predicate.Compile())
    .Where(s => s.DocumentType == type)
    .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