Ravi V
Ravi V

Reputation: 101

How To run LINQ queries with Type of <T>

This is My Class which is basically Closed Coupled To Type I want to Generalise this Class.

So I can use with all the Class

one thing is common that there will be a Name field while invoking this method

    public class CustomSuggestionProvider : ISuggestionProvider
    {
        private const int batchSize = 30;
        private string _criteria = string.Empty;
        private int _skipCount;

        private readonly ObservableCollection<LanguageItem> _observableCollection;
        private readonly List<LanguageItem> _source;

        public CustomSuggestionProvider(ObservableCollection<LanguageItem> observableCollection, List<LanguageItem> source)
        {
            _observableCollection = observableCollection;
            _source = source;
        }

        public bool HasMoreSuggestions { get; private set; } = true;

        public Task<IList<object>> GetSuggestionsAsync(string criteria, CancellationToken cancellationToken)
        {
            _criteria = criteria;
            var newItems = _source.Where(x => x.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
            if (cancellationToken.IsCancellationRequested)
                return null;
            HasMoreSuggestions = newItems.Count > batchSize;
            _skipCount = batchSize;
            return Task.FromResult<IList<object>>(newItems.Take(batchSize).Cast<object>().ToList());
        }

     
    }

I Just made it Generalised like this.

Made all the closely coupled class to Type T.

Kindly have a look.

 public class CustomSuggestionProvider<T> :  ISuggestionProvider
    {
        private const int batchSize = 30;
        private string _criteria = string.Empty;
        private int _skipCount;
        
        private readonly ObservableCollection<T> _observableCollection;
        private readonly List<T> _source;

        public CustomSuggestionProvider(ObservableCollection<T> observableCollection, List<T> source)
        {
            _observableCollection = observableCollection;
            _source = source;
        }
 

        public bool HasMoreSuggestions { get; private set; } = true;
]


        public Task<IList<object>> GetSuggestionsAsync(string criteria, CancellationToken cancellationToken)
        {
            _criteria = criteria;
                var newItems = _source.Where(x =>Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
            if (cancellationToken.IsCancellationRequested)
                return null;
            HasMoreSuggestions = newItems.Count > batchSize;
            _skipCount = batchSize;
            return Task.FromResult<IList<object>>(newItems.Take(batchSize).Cast<object>().ToList());
        }

        public Task<IList<object>> GetSuggestionsAsync(CancellationToken cancellationToken)
        {
            var newItems = _source.Where(x => x.Name.StartsWith(_criteria)).Skip(_skipCount).ToList();
            if (cancellationToken.IsCancellationRequested)
                return null;
            HasMoreSuggestions = newItems.Count > batchSize;
            _skipCount += batchSize;
            return Task.FromResult<IList<object>>(newItems.Take(batchSize).Where(x => !_observableCollection.Any(y => y.Id == x.Id)).Cast<object>().ToList());
        }
    }

I just stuck when at this statement

The 'Name' does not exist here

var newItems = _source.Where(x => x.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();

Can you please help troubleshoot this.

Thanks in Advance.

Upvotes: 0

Views: 128

Answers (3)

Tariq Hajeer
Tariq Hajeer

Reputation: 348

You sould to write an interface like this

public interface IName
{
    string Name { set; get; }
}

note : all you'r classes should implement from IName

write function like this

public static IList<IName> Names(IList<IName> names, string _criteria)
{
    return names.Where(c => c.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
}

and this for IEnumerable as extension method

public static class IEnumerableExtension
{
    public static IEnumerable<IName> Filter(this IEnumerable<IName> names, string _criteria)
    {
        return names.Where(c => c.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0);
    }
}

Upvotes: 2

AakashM
AakashM

Reputation: 63398

Nicer (in my opinion) than either the 'require an IName interface' or 'use reflection' options is this: when constructing a CustomSuggestionProvider<T>, pass in a Func<T, string> which the provider can invoke whenever it needs a T's name.

Add in CustomSuggestionProvider<T>

Func<T, string> nameGetter;

Initialise it in the constructor, with callers passing in something like class => class.Name.

Then whenever you need a T's name, you just invoke this function. So your

var newItems = _source
    .Where(x => x.Name.IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0)
    .ToList();

becomes

var newItems = _source
    .Where(x => nameGetter(x).IndexOf(_criteria, StringComparison.OrdinalIgnoreCase) >= 0)
    .ToList();

Upvotes: 0

Dmitry Kolchev
Dmitry Kolchev

Reputation: 2216

You has two options. First - object must inherit interface/class that contains Name property. Second you can use reflection and access the Name property with compiled lambda

    List<ObjectWithName> items = new List<ObjectWithName>()
    {
        new ObjectWithName { Name = "Hello World #1"},
        new ObjectWithName { Name = "Hello World #2"}
    };

    var result = FirstOption(items, "Hello");
    result = SecondOption(items, "Hello");
    ...

    public static List<T> FirstOption<T>(IList<T> source, string criteria) where T: IObjectWithName
    {
        return source.Where(t => t.Name.IndexOf(criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
    }

    public static List<T> SecondOption<T>(IList<T> source, string criteria)
    {
        PropertyInfo propertyInfo = typeof(T).GetProperty("Name");
        MethodInfo getterMethodInfo = propertyInfo.GetGetMethod();
        ParameterExpression entity = Expression.Parameter(typeof(T));
        MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo);
        UnaryExpression castToObject = Expression.Convert(getterCall, typeof(string));
        LambdaExpression lambda = Expression.Lambda(castToObject, entity);
        var functionThatGetsValue = (Func<T, string>)lambda.Compile();

        return source.Where(t => functionThatGetsValue(t).IndexOf(criteria, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
    }
}

public class ObjectWithName: IObjectWithName
{
    public string Name { get; set; }
}

public interface IObjectWithName
{
    string Name { get; }
}

For optimization you can compile lambda only once for type and cache results

Upvotes: 2

Related Questions