Reputation: 1579
I created the following extension methods in a .Net 5 webapi project :
public static class QueryableExtensions
{
public static IOrderedQueryable<TSource> Filter<TSource>(this IOrderedQueryable<TSource> query, IEnumerable<string> filter) where TSource : IModel
{
throw new NotImplementedException();
}
public static IOrderedQueryable<ApplicationUser> Filter(this IOrderedQueryable<ApplicationUser> query, IEnumerable<string> filter)
{
return query
.Where(x => filter.Any(str => !string.IsNullOrEmpty(x.Email) && x.Email.Contains(str, StringComparison.CurrentCultureIgnoreCase)))
.OrderBy(x => x.Id);
}
}
The generic one is just a 'placeholder'. I use these methods in some service classes in a service-repository pattern.
The base service class :
public class DataServiceBase<T> : IDataService<T> where T : class, IModel, new()
{
public IValidationDictionary ValidationDictionary { get; }
protected readonly IRepository<T> Repo;
public DataServiceBase(IRepository<T> repo, IValidationDictionary validationDictionary)
{
Repo = repo;
ValidationDictionary = validationDictionary;
}
public virtual IOrderedQueryable<T> Read(ApplicationContext applicationContext)
{
return Repo.Read().OrderBy(x => x.Id);
}
public virtual IOrderedQueryable<T> Read(ApplicationContext applicationContext, ReadListSettings readListSettings)
{
var query = Read(applicationContext);
if (readListSettings.Filter != null)
{
// HERE THE EXTENSIONS METHOD IS USED
query = query.Filter(readListSettings.Filter.Process());
// HERE THE EXTENSIONS METHOD IS USED
}
return query;
}
}
And a specific one :
public partial class ApplicationUserService : DataServiceBase<ApplicationUser>
{
public ApplicationUserService(
IRepository<ApplicationUser> repo,
IValidationDictionary validationDictionary
) : base(repo, validationDictionary) {}
public override IOrderedQueryable<ApplicationUser> Read(ApplicationContext applicationContext)
{
return Repo
.Read()
.Include(x => x.User2UserGroups)
.ThenInclude(x => x.UserGroup)
.ThenInclude(x => x.Tenant)
.OrderBy(x => x.Email);
}
}
I would expect that when calling ApplicationUserService.Read(applicationcontext, readlistsettings) the specific version of the extensionmethod is called. I checked the type and the generic method gets the correct class as its TSource.
I tried adding the following to my AppUserService, but still the TSource-version gets called.
public override IOrderedQueryable<ApplicationUser> Read(ApplicationContext applicationContext, ReadListSettings readListSettings)
{
return base.Read(applicationContext, readListSettings);
}
Any suggestions ?
Upvotes: 0
Views: 120
Reputation: 37800
Extension methods are not about polymorphism. Given these models and settings:
public interface IModel
{
public int Id { get; set; }
}
public class ApplicationUser : IModel
{
public int Id { get; set; }
public string Email { get; set; }
}
public class ReadListSettings
{
public IEnumerable<string> Filter { get; set; }
}
your custom filtering could be implemented like this:
public abstract class DataServiceBase<T>
where T : IModel
{
public IOrderedQueryable<T> Read()
{
// gets queryable from repository somehow
return Enumerable.Empty<T>().AsQueryable().OrderBy(x => x.Id);
}
public IOrderedQueryable<T> Read(ReadListSettings readListSettings)
{
var query = Read();
if (readListSettings.Filter != null)
{
// use custom filtering in descendatns here
query = Filter(query, readListSettings.Filter);
}
return query;
}
protected virtual IOrderedQueryable<T> Filter(IOrderedQueryable<T> query, IEnumerable<string> filter) => query;
}
public partial class ApplicationUserService : DataServiceBase<ApplicationUser>
{
protected override IOrderedQueryable<ApplicationUser> Filter(IOrderedQueryable<ApplicationUser> query, IEnumerable<string> filter)
{
return query
.Where(x => filter.Any(str => !string.IsNullOrEmpty(x.Email) && x.Email.Contains(str, StringComparison.CurrentCultureIgnoreCase)))
.OrderBy(x => x.Id);
}
}
(I've simplified your types, and left relevant code only)
Upvotes: 2