CreateInstance inside Extension Method not allowed? - entity-framework-core

I'm trying to convert my IQueryable<TEntity> results to IEnumerable<TDomainModel> via an extension method.
IEnumerable<DeviceModel> resultEntities = _unitofwork.PersonDeviceRepository
.GetUnassociatedDevices()
.AddFilters(deviceByTypeQueryObject.AsExpression())
.ToDomainModels<DeviceModel,Device>();
The IQueryable Extension is:
public static IEnumerable<TDomainModel> ToDomainModels<TDomainModel, TEntity>(this IQueryable<TEntity> queryableEntities)
{
IEnumerable<TDomainModel> domainModels =
queryableEntities
.Select(indiv => (TDomainModel)Activator.CreateInstance(typeof(TDomainModel), indiv));
return domainModels;
}
It compiles but errors in runtime:
System.InvalidOperationException: The client projection contains a reference to a constant expression of 'System.RuntimeType' which is being passed as an argument to the method 'CreateInstance'.
But it works if the Activator.CreateInstance conversion is NOT done in as part of an extension method. ie: in a regular method
Why is this happening?

Found the issue:
I was feeding an IQueryable to the Extension method, but it doesn't work. It had nothing to do with the CreateInstance.
It worked after first converting the IQueryable to AsEnumerable, then fed it to the ToDomainModel extension method.
IEnumerable<DeviceModel> resultEntities = _unitofwork.PersonDeviceRepository.GetUnassociatedDevices().AddFilters(deviceByTypeQueryObject.AsExpression()).AsEnumerable().ToDomainModels<Device,DeviceModel>();
The extension method was rewritten for IEnumerable instead of IQueryable
public static IEnumerable<TDomainModel> ToDomainModels<TEntity, TDomainModel>(this IEnumerable<TEntity> enumerableEntities)
{
IEnumerable<TDomainModel> domainModels = queryableEntities.Select(indiv => (TDomainModel)Activator.CreateInstance(typeof(TDomainModel), indiv));
return domainModels;
}

Related

Non-recognized member in lambda expression of EF Core ThenInclude in Extension method

I'm trying to write an extension method to include a certain property (text element, themselves containing a collection of translations) that are present in many of my entity models.
I had no problem with the .Include function:
public static IIncludableQueryable<T, IEnumerable<Translation>> IncludeTextBitWithTranslations<T>(this IQueryable<T> source, Expression<Func<T, TextBit>> predicate) where T: class
{
var result = source.Include(predicate).ThenInclude(t => t.Translations);
return result;
}
And tests proved successful.
Now, in some cases, I have entities that have all their texts in a child - for example Article entity has an ArticleInfo property that contains a few text elements. So I figure I just needed to do another extension that was a ThenInclude instead. With a few differences I finally get this :
public static IIncludableQueryable<TEntity, ICollection<Translation>> ThenIncludeTextBitWithTranslations<TEntity, TPreviousProperty, TextBit>(this IIncludableQueryable<TEntity, TPreviousProperty> source, Expression<Func<TPreviousProperty, TextBit>> predicate) where TEntity: class
{
var result = source.ThenInclude(predicate)
.ThenInclude(t => t.Translations);
return result;
}
And now I get this error:
'TextBit' does not contain a definition for 'Translations' and no extension method 'Translations' accepting an argument of 'TextBit' type was found
This error appears on the last lambda expression t => t.Translations.
This error is extremely weird for me, I've been looking all over the internet for some help on the matter but I was unsuccessful.
I tried forcing the type to the ThenInclude by adding them manually :
var result = source.ThenInclude(predicate)
.ThenInclude<TEntity, TextBit, ICollection<Translation>>(t => t.Translations);
but without success.
Does anyone have some clues as to why?
I'm very much at a loss here
You have extra type parameter TextBit in second one (ThenIncludeTextBitWithTranslations<TEntity, TPreviousProperty, TextBit>), so it is considered as a generic type, not an actual one, remove it:
public static IIncludableQueryable<TEntity, ICollection<Translation>> ThenIncludeTextBitWithTranslations<TEntity, TPreviousProperty>(this IIncludableQueryable<TEntity, TPreviousProperty> source, Expression<Func<TPreviousProperty, TextBit>> predicate) where TEntity: class
{
var result = source.ThenInclude(predicate).ThenInclude(t => t.Translations);
return result;
}

2nd level caching EF codeFirst and Predicate not working on a where clause

I have a question about caching with Entity Framework code first.
I need to cache my query's results and I came about something that I didn't know.
Func<T, bool> predicate does not work when filtering whilst Expression<Func<T, bool>> predicate does.
Maybe I'm missing the obvious.
Here is my scenario:
Whenever I call a method, e.g."GetOrders", I use a method called "GetCachedModels" internally to get the cached version.
When subsequently many calls are made with
"GetOrders(customerNo)" it checks the cache and the get it from there if it's there. That is the theory.
However when using Func predicate it does not find the item, but when using the Expression version it does?
My question is how do you use "Expressions" with a Where clause on a list?
The other solution would be to have a method for each search, e.g. "myservice.GetCustomer(etc..) or myservice.GetOrders(etc..) instead of generic
myservice.GetAll(); Which means adding many methods to the interface.
mycode:
public interface IGenericRepository
{
IList<T> GetAll<T>() where T : class;
IList<T> Find<T>(Func<T, bool> predicate) where T : class; //I use this so that I could use the predicate in an where clause against a list.
etc....
}
In my repository I have something like:
public IList<T> Find<T>(Func<T, bool> predicate) where T : class
{
List<T> models = GetCachedModels<T>().ToList();
var result= models.Where(predicate).ToList(); --Does not work!!! tried also with(expression.Compile()).ToList(); !!still no results!!!!
return result;
}
internal IList<T> GetCachedModels<T>() where T : class
{
IList<T> models;
Type typeParameterType = typeof(T);
string cacheName = string.Format("{0}Cache", typeParameterType.Name);
object cacheView = DbCache.Get(cacheName);
if (cacheView == null)
{
models = Set<T>().ToList();
DbCache.Add(cacheName, models, DateTime.Now.AddHours(1));
}
else
{
models = (IList<T>)cacheView;
}
return models;
}
//below method works but cannot use cache!!!!
public IList<T> Find<T>(Expression<Func<T, bool>> predicate) where T : class
{
return Set<T>().Where(predicate).ToList();
}
Expression predicates works only for IQueryable interface. List doesn't inherit it, so if you want to use this expression, you need to return IQueryable in GetCachedModels method, and return Set, so it can query this data. And then you can place it.
Otherwise if you want to cache all items from Set, then you need to pass Func instead of Expression, and then use it in Where extension method, like this - http://dotnetfiddle.net/5YsIy3

Can you use Lambda expressions against ObjectSets in Entity Framework?

I am using EF5, although I am sure it is a more general EF Question.
I cannot get the following to work. I keep getting casting error:
Unable to cast object of type 'System.Data.Objects.ObjectQuery`1[StdOrgUser]' to type 'System.Data.Objects.ObjectSet`1[StdOrgUser]'.
For the code:
public ObjectSet<StdOrgUser> StdOrgUser
{
get
{
if ((_StdOrgUser == null))
{
_StdOrgUser = base.CreateObjectSet<StdOrgUser>("StdOrgUser");
_StdOrgUser = (ObjectSet<StdOrgUser>) _StdOrgUser.Where(r => r.IsActive == false);
}
return _StdOrgUser;
}
}
It compiles fine. Intellisense enables me to choose LINQ operators etc. It is when I run it, that I get the above runtime error.
Where am I going wrong?
Many thanks for any help.
The ObjectSet class implements (amongst other things) IQueryable and IEnumerable, both of these interfaces have an extension method Where, see here and here. Neither IQueryable nor IEnumerable (which are the respective return types of the extension methods) can be cast back to ObjectSet.
The following line of code cannot be evaluated until run time:
_StdOrgUser = (ObjectSet<StdOrgUser>) _StdOrgUser.Where(r => r.IsActive == false);
but if you remove the cast the code will not compile:
_StdOrgUser = _StdOrgUser.Where(r => r.IsActive == false);
UPDATE
For querying you could change the return type of StdOrgUsers from ObjectSet to IQueryable but you lose all the other methods such as Add, Attach etc. You can't apply a standard filter using this technique. You could have an extension method called ActiveUsers()
public static IQueryable<StdOrgUser> ActiveUsers(this ObjectSet<StdOrgUser> users)
{
return users.Where(r => r.IsActive == false);
}
what you need to do is remember to use it in each query (not very pretty but it does clearly show intent)
var results = myContext
.StdOrgUser
.ActiveUsers()
.Where(//some filter);

Using function result inside expression function used by a predicate

I am trying to use predicateBuilder with next expression definition but I always got the message
"LINQ to Entities does not recognize the method 'puedeConsultar' method, and this method cannot be translated into a store expression."
I think i understand more less this problem, but i don´t know how to solve it.
private static readonly IDictionary<int, List<string>> permisosAccesoSolicitudesEstado = new Dictionary<int, List<string>>(){{0, new List<string>(){"A"}}, {1, new List<string>(){"B"}}};
private static bool esPermisoConcedido(List<string> usuariosPermitidos, string erfilUsuario)
{
return usuariosPermitidos.Any(x => x.Equals(perfilUsuario) || perfilUsuario.StartsWith(x + "|") || perfilUsuario.EndsWith("|" + x));
}
public static bool puedeConsultar(int estadoActual, string perfilUsuario)
{
List<string> usuariosPermitidos = permisosAccesoSolicitudesEstado[estadoActual];
return esPermisoConcedido(usuariosPermitidos, perfilUsuario);
}
public static bool puedeConsultar(string estadoActual, string tipoUsuario)
{
return puedeConsultar(Convert.ToInt32(estadoActual), tipoUsuario);
}
public Expression<Func<Solicitud, Boolean>> predicadoEstadoCorrectoSolicitud(string perfil)
{
return x=> EstadosSolicitud.puedeConsultar(x.estado, perfil);
}
//Instantiated by reflection, this works fine
MethodInfo method = .....
Expression<Func<T, bool>> resultado = ConstructorPredicados.True<T>();
resultado = ConstructorPredicados.And(resultado, method);
objectSet.Where(resultado).ToList();
Note:
ConstructorPredicados is based in Monty´s Gush "A universal PredicateBuilder" on http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/
Thanks in advance
You cannot do that. Your puedeConsultar is .NET function. You cannot execute .NET functions in Linq-to-entities query. When you use method in Linq-to-entities you can use only methods which has direct mapping to SQL. It means that method in the query is only placeholder which is translated to execution of some SQL function. There is set of predefined method mappings called cannonical functions and you can map your own SQL function when using EDMX but in your case you will most probably have to first load data to application by using ToList and after that execute predicadoEstadoCorrectoSolicitud on materialized result.

Entity Framework and repository pattern problem

I am using generic repository pattern with methods:
private ObjectQuery<T> ObjectQueryList()
{
var list = CamelTrapEntities.CreateQuery<T>(EntitySetName);
return list;
}
public IQueryable<T> List()
{
return ObjectQueryList();
}
Metod List() returns IQueryable<T>, becase IQueryable<T> is easy to mock. I also have extension method:
public static IQueryable<T> Include<T>(this IQueryable<T> obj, string path)
{
if (obj is ObjectQuery<T>)
(obj as ObjectQuery<T>).Include(path);
return obj;
}
This method is used outside of repository to get entity list with navigation properties already loaded, for example: List.Include("CreatedBy"). The problem is that it doesn't work. All includes are ignored. when I change List() method to
public ObjectQuery<T> List()
{
return ObjectQueryList();
}
everything works fine.
How should I implement repository pattern to be able to execute more complex queries?
Reflector gave me an answer:
public ObjectQuery<T> Include(string path)
{
EntityUtil.CheckStringArgument(path, "path");
return new ObjectQuery<T>(base.QueryState.Include<T>((ObjectQuery<T>) this, path));
}
Include returns new ObjectQuery object and my Include function returned old object. Changing to
public static IQueryable<T> Include<T>(this IQueryable<T> obj, string path)
{
if (obj is ObjectQuery<T>)
return (obj as ObjectQuery<T>).Include(path);
return obj;
}
solved the problem. Few hours lost and I hate Entity Framework more:)
It made me also realise that I should create another List function with Include parameter and not allow doing includes outside repository.
Here is the most comprehensive Repository pattern implementation I've seen for EF. I can't say for sure if it will allow you to do Include(), but if I read the implementation right it should.
With EntityFramework 4.1, DbExtensions (System.Data.Entity.DbExtensions) resolves this problem, and natively adds both .Include([string path]) and .Include([property expression]) for any IQueryable<T>.
Just be sure that the project using your repository references EntityFramework, and, as with any extension methods, specify using System.Data.Entity; in your class file to gain access to these extensions.