简体   繁体   中英

Create Generic method in generic repository consumer class

I want to create generic method in generic repository consumer class.

Here is my generic method in generic repository class:

public class CosmosDBRepository<T> : ICosmosDBRepository<T> where T : class
    {
     public async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> orderByDesc, int takeCount = -1)
    
            {
                var criteria = _container.GetItemLinqQueryable<T>(true)
                    .Where(predicate)
                    .OrderByDescending(orderByDesc)
                    .ToFeedIterator();
    
                var query = criteria;
    
                var results = new List<T>();
                while (query.HasMoreResults)
                {
                    if (takeCount > -1 && results.Count >= takeCount) break;
                    results.AddRange(await query.ReadNextAsync());
                }
    
                return results;
            }
}

Generic Repository consumer class:

 public class SubscriptionRepository : CosmosDBRepository<Subscription>, ISubscriptionRepository
    {
        public SubscriptionRepository(
            ICosmosDBClient client
            ) : base(client)
        {

        }

        
        public async Task<List<T>> GetSubscriptions<T, TE>(
            TE eventItem,
            params SubscriptionAction[] subscriptionAction)
                where T : Subscription
                where TE : Event
        {
            Expression<Func<T, bool>> predicate = (x) => x.EventType == eventItem.EventType
                            && x.IsActive;

            predicate = predicate.And(x => subscriptionAction.Contains(x.Action));

            if (!string.IsNullOrEmpty(eventItem.PayerNumber))
            {
                predicate = predicate.And(x => x.PayerNumber == eventItem.PayerNumber);
            }
            else if (!string.IsNullOrEmpty(eventItem.AccountNumber))
            {
                predicate = predicate.And(x => x.AccountNumber == eventItem.AccountNumber);
            }

            var result = await GetItemsAsync(predicate, o => o.PayerNumber);

            return result.ToList();
        }
    }

Now I want to create generic method GetSubscriptions in SubscriptionRepository class.

Could you please suggest how I can achieve this?

Currently I am getting following compile time error:

cannot convert from 'System.Linq.Expression<System.Func<T,bool>>' to 'System.Linq.Expression.Expression<System.Func<FleetHub.Notifications.Domain.CosmosDB.Containers.Subscription,bool>>'

There are at least two issues here, both of the same nature - you trying to make a generic method to wrap a concrete CosmosDBRepository<Subscription>.GetItemsAsync function. First one can be solved changing predicate to:

Expression<Func<Subscription, bool>> predicate = x => x.EventType == eventItem.EventType && x.IsActive; 

But it will not solve issue that CosmosDBRepository<Subscription>.GetItemsAsync will return collection of Subscription 's and not collection of arbitrary Subscription descendant. If you need GetSubscriptions still be generic you will need either to filter and cast result of GetItemsAsync :

return result.OfType<T>().ToList();

Or provide a mapper function to method:

 public async Task<List<T>> GetSubscriptions<T, TE>(
        TE eventItem,
        Func<Subscription, T> mapper, 
        params SubscriptionAction[] subscriptionAction)
            where T : Subscription
            where TE : Event
    {
        .....
        return result.Select(mapper).ToList();
    }
 

Maybe you should use

Expression<Func<Subscription, bool>> predicate = (x) => x.EventType == eventItem.EventType
                        && x.IsActive;

Since you are using a generic type T instead of Subscription (even if you check that T is supposed to be Subscription or a child class) the expression cannot be assigned.

And in your case I don't see the benefits to use generic in the GetSubscriptions instead of the real type directly.

Check this example:

   public class Test
   {
   }

   Expression<Func<Test, bool>> temp1 = (t) => true;
   Expression<Func<object, bool>> temp2 = temp1;

This example will fail at the second line. Even is Test is a child class of object (as all class in .NET)

Edit

As explained here: https://docs.microsoft.com/en-us/dotnet/api/system.func-2?view=netcore-3.1

The T of Func (the first generic type parameter) have the "in" keyword. Which means:

This type parameter is contravariant. That is, you can use either the type you specified or any type that is less derived.

Since T could be a child of Subscription (let's say "ChildSubscription"). When you try to assign Func to Func your try to assign a more derived.

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