I'm trying to implement a custom LinQ Count() method. Basically what I'm trying to achieve here is before calling the Count method, I want to filter out all elements that have the property IsDeleted set to true. So, I created an extension class and I added these methods:
public static int Count2<T>(this IEnumerable<T> source, Func<T, bool> selector)
where T : Model
{
return source.Where(x => !x.IsDeleted).Count(selector);
}
public static int Count2<T>(this IQueryable<T> source, Expression<Func<T, bool>> selector)
where T : Model
{
return source.Where(x => !x.IsDeleted).Count(selector);
}
public static int Count2<T>(this IEnumerable<T> source)
where T : Model
{
return source.Count(x => !x.IsDeleted);
}
public static int Count2<T>(this IQueryable<T> source)
where T : Model
{
return source.Count(x => !x.IsDeleted);
}
This works just find for local collections, but when executing this command for instance:
ListOfModels.Sum(x => x.PropertyThatIsAList.Count2())
and ListOfModels is an instance of IQueryable, i.e. it has to be executed in the database, it gives me this error:
The LINQ expression 'Sum()' could not be translated and will be evaluated locally.
I looked around on the web and I saw some answers saying I have to implement the IQueryableProvider but I think there is no need to go into such complicated path since the Sum() and Count() are translatable, I only need to count conditionally. Is it possible, and if it is, can anyone give me a clue on how to do it?
I suggest you instead of customizing all LinQ methods use an extended method like Validate():
public static IEnumerable<T> Validate<T>(this IEnumerable<T> list) where T: IDeleteable
{
return list.Where(w => !w.IsDeleted);
}
That IDeleteable interface is like this:
public interface IDeleteable
{
bool IsDeleted { get; set; }
}
Then use it before other methods.
Related
I have the following classes:
class Foo
{
public int X[];
public int Y[];
public int Z[];
}
class Bar
{
public int X;
public int Y;
public int Z;
}
I wish to create the following AutoMapper map:
CreateMap<Foo, IEnumerable<Bar>>
This is to map a single Foo object to a collection of Bar such that Foo.X[i] and Foo.Y[i] will map to Bar[i].X and Bar[i].Y. The arrays will always be the same length. Is this possible with AutoMapper using built-in functionality? Ideally, I'd like to avoid having to explicitly map each member programmatically.
As an added bonus, I would also like to support postfixes on the source using RecognizePostfixes("Postfix") and the following version of Foo:
class Foo
{
public int XPostfix[];
public int YPostfix[];
public int ZPostfix[];
}
With a pointer in the right direction from #LucianBargaoanu together with this answer on another question, I was able to come up with a solution using a ITypeConverter and an IEnumerable extension method.
This is the ITypeConverter:
class TransposeConverter<TSource, TDestination> : ITypeConverter<TSource, IEnumerable<TDestination>> where TDestination : class, new()
{
public IEnumerable<TDestination> Convert(TSource source, IEnumerable<TDestination> destination, ResolutionContext context)
{
// Zip all the member collections from the source object together into a single collection then map to the destination based on the property names.
return typeof(TSource).GetProperties()
.Select(p => ((IEnumerable)p.GetValue(source)).Cast<object>().Select(item => (item, p.Name)))
.Zip(s => context.Mapper.Map<TDestination>(s.ToDictionary(k => k.Name, e => e.item)));
}
}
This is the Zip extension method:
public static IEnumerable<TResult> Zip<T, TResult>(this IEnumerable<IEnumerable<T>> collections, Func<IEnumerable<T>, TResult> resultSelector)
{
var enumerators = collections.Select(s => s.GetEnumerator()).ToArray();
while (enumerators.All(e => e.MoveNext()))
{
yield return resultSelector(enumerators.Select(e => e.Current));
}
}
However, this only solves the first part of the question. It doesn't resolve the "added bonus" part where I wish to handle postfixes on the property names. I've raised another question for this.
I have a generic repository, something like this:
public IEnumerable<T> SelectAll()
{
return table.ToList();
}
public T SelectByID(object id)
{
return table.Find(id);
}
public void Insert(T obj)
{
table.Add(obj);
}
This works fine for the basic CRUD, but now I need to search an entity (table) against an user entered searchterm. Is it at all possible to do it with something like:
public IEnumerable<T> SelectAll(T obj, string searchText, string columnName)
{
// I am not sure what code to write here... It should give me all the records that contain the search term.
// I was thinking something like this could be made to work...but I need help with it.
return table.GetType().GetProperty(columnName).GetValue())ToList();
}
Use a Lambda Expression as the argument
public virtual async Task<List<T>> SearchBy(Expression<Func<Table, bool>> searchBy)
{
return await _ctx.Set<Table>().Where(searchBy).ToListAsync();
}
with this, you will call the search with a table expression argument returning a boolen
This is what I am using now:
public IEnumerable<T> SelectAll(Expression<Func<T, bool>> predicate)
{
IEnumerable<T> searchResult = context.Set<T>().Where(predicate);
return searchResult;
}
And the method call:
var lstResult = objRepo.SelectAll(x=>x.ColumnName.Contains(searchText));
i want to create a method that can be used with lambda in that way:
return Method<MyClass>(x => x.PropName1, x.PropName2,...);
inside it i have to use tha propName to eager load those reference field via nhibernate:
return session.Query<MyClass>()
.Fetch(c => c.PropName1)
.Fetch(c => c.PropName2).ToList();
i look into linq source code to find some similar and went here:
public static void ListEager<TEntity>(IEnumerable<Func<TEntity, TKey>> fields)
but it's simply not correct.
how can it be done?
You can do like this, implement IGeneric interface and Generic class, with generic method GetList, i use this generic method and working very well.
public interface IGenericDataRepository<T> where T : class
{
IList<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
}
public class GenericDataRepository<T> : IGenericDataRepository<T> where T : class
{
public virtual IList<T> GetList(Func<T, bool> where,
params Expression<Func<T, object>>[] navigationProperties)
{
List<T> list;
using (var dbQuery = new session.Query<T>())
{
//Apply eager loading
foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
dbQuery = dbQuery.Fetch<T, object>(navigationProperty);
list = dbQuery
.AsNoTracking()
.Where(where)
.ToList<T>();
}
return list;
}
}
To use it you need create repository class for any entity, here is example with my ProductRepository class
public interface IProductRepository:IGenericDataRepository<Product>
{
////
}
public class ProductRepository:GenericDataRepository<Product>,IProductRepository
{
////
}
i switch to queryover to get more power :D
public IEnumerable<TEntity> List(params Expression<Func<TEntity, object>>[] eagerFields)
{
var query = _session.QueryOver<TEntity>();
query = AddReferenceFetch(query, eagerFields);
return query.TransformUsing(Transformers.DistinctRootEntity).List();
}
private IQueryOver<TEntity, TEntity> AddReferenceFetch(IQueryOver<TEntity, TEntity> query, params Expression<Func<TEntity, object>>[] eagerFields)
{
foreach (Expression<Func<TEntity, object>> field in eagerFields)
query = query.Fetch(field).Eager;
return query;
}
in this way i can manage reference or hasmany without problem
i left #mww as accepted answer because the main idea is his
I would like something like this:
public int NumberStudent()
{
int i = 0;
if (db.Tbl_Student.ToList().Count() > 0)
i = db. Tbl_Student.Max(d => d.id);
return i;
}
However, I would like to use it on any table:
public int FindMaxId(string TableName)
{
int i =0;
if ('db.'+TableName+'.ToList().Count() > 0' )
i = db. TableName.Max(d => d.id);
return i ;
}
I know it is wrong, but I'm not sure how to do it.
You can use the IEnumerable/IQueryable extension method DefaultIfEmpty for this.
var maxId = db.Tbl_Student.Select(x => x.Id).DefaultIfEmpty(0).Max();
In general, if you do Q.DefaultIfEmpty(D), it means:
If Q isn't empty, give me Q; otherwise, give me [ D ].
Below I have written a simple wrapper around the existing Max extension method that allows you provide an empty source (the table you were talking about).
Instead of throwing an exception, it will just return the default value of zero.
Original
public static class Extensions
{
public static int MaxId<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int> selector)
{
if (source.Any())
{
return source.Max(selector);
}
return 0;
}
}
This was my attempt, which as noted by Timothy is actually quite inferior. This is because the sequence will be enumerated twice. Once when calling Any to check if the source sequence has any elements, and again when calling Max.
Improved
public static class Extensions
{
public static int MaxId<TSource>(this IQueryable<TSource> source, Func<TSource, int> selector)
{
return source.Select(selector).DefaultIfEmpty(0).Max();
}
}
This implementation uses Timothy's approach. By calling DefaultIfEmpty, we are making use of deferred execution and the sequence will only be enumerated when calling Max. In addition we are now using IQueryable instead of IEnumerable which means we don't have to enumerate the source before calling this method. As Scott said, should you need it you can create an overload that uses IEnumerable too.
In order to use the extension method, you just need to provide a delegate that returns the id of the source type, exactly the same way you would for Max.
public class Program
{
YourContext context = new YourContext();
public int MaxStudentId()
{
return context.Student.MaxId(s => s.Id);
}
public static void Main(string[] args)
{
Console.WriteLine("Max student id: {0}", MaxStudentId());
}
}
public static class Extensions
{
public static int MaxId<TSource>(this IQueryable<TSource> source, Func<TSource, int> selector)
{
return source.Select(selector).DefaultIfEmpty(0).Max();
}
}
db.Tbl_Student.Aggregate(0, (maxId, s) => Math.Max(maxId, s.Id))
or
db.Tbl_Student.Max(s => (int?)s.Id) ?? 0
I have a situation where I need to dynamically build up a list of filters to apply to a list of objects. Those objects can be anything that implements an interface which contains all of the properties I need to filter on.
public interface IStuff
{
bool SuitableForSomething { get; set; }
bool SuitableForSomethingElse { get; set; }
}
public class SomeStuff : IStuff
{
...
}
public class SomeOtherStuff : IStuff
{
...
}
I have a list of criteria defined like so ...
public List<Expression<Func<IStuff, bool>>> Criteria { get; private set; }
and add criteria like so ...
Criteria.Add(x => x.SuitableForSomething);
Criteria.Add(x => x.SuitableForSomethingElse);
I then apply the criteria to my query like so ...
var stuff= _stuffCache
.GetAll()
.AsQueryable()
.ApplyCriteria(Criteria);
which uses the following extension method ...
public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<IStuff, bool>>> criteria)
where T : IStuff
{
foreach (var expression in criteria)
{
stuff = Queryable.Where(stuff, expression);
}
return stuff;
}
The compiler is telling me ...
cannot convert from
'System.Linq.Expressions.Expression<System.Func<IStuff,bool>>'
to
'System.Linq.Expressions.Expression<System.Func<T,int,bool>>'
When I hover over the red line under the error in the IDE it's saying it cannot resolve method between
IQueryable<IStuff> Where<IStuff>(this IQueryable<IStuff>, Expression<Func<IStuff, bool>>) in class Queryable
and
IQueryable<T> Where<T>(this IQueryable<T>, Expression<Func<T,int,bool>>) in class Queryable
If I try casting the expression to Expression<Func<T, bool>>, which ought to work as T is constrained to implement the IStuff interface. I get
Cannot cast expression of type 'Expression<Func<IStuff, bool>>' to type 'Expression<Func<T, bool>>'
EDIT
Thanks to Raphaƫl's answer I fixed the extension method and eventually found the real problem I had, which was a casting problem when I was calling the code. Easily fixed by adding a .Cast<SomeStuff>() after the ApplyCriteria call.
Before
var stuff= _stuffCache
.GetAll()
.AsQueryable()
.ApplyCriteria(Criteria);
After
var stuff= _stuffCache
.GetAll()
.AsQueryable()
.ApplyCriteria(Criteria)
.Cast<SomeStuff>();
change second parameter type to List<Expression<Func<T,bool>>> (T instead of IStuff)
public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<T, bool>>> criteria)
where T : IStuff
{
foreach (var expression in criteria)
{
stuff = Queryable.Where(stuff, expression);
//or stuff = stuff.Where(expression), as Where is an Extension method;
}
return stuff;
}
and your method can be (thx resharper), rewritten to
public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<T, bool>>> criteria)
where T : IStuff
{
return criteria.Aggregate(stuff, (current, expression) => current.Where(expression));
}