Entity Framework 4.1: Repository with "DefaultOrder Property" - entity-framework

When using the following code (simplified), I get the error
"Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types."
in the line with the return statement:
public MyRepository<Post>
{
public Expression<Func<Post, object>> DefaultOrder;
public MyRepository()
{
DefaultOrder = p => p.PublishedOn;
}
public IQueryable<Post> All()
{
var entities = new MyDbContext().Set<Post>();
return entities.OrderByDescending(DefaultOrder);
}
}
I used the same code with db4o/db4o.Linq instead of Entity Framework and there was no problem.
So here my questions:
Why is this error happening?
Is there an alternative solution, which lets me define my DefaultOrder in the same (a similar) manner as I do now?
EDIT:
Found a solution, that works for me, replaced the default order expression with an order method:
public MyRepository<T>
{
public Func<IQueryable<T>, IOrderedQueryable<T>> DefaultOrderMethod;
public MyRepository()
{
DefaultOrderMethod = o => o.OrderBy(x => x.PublishedOn);
}
public IQueryable<T> All()
{
var entities = new MyDbContext().Set<T>();
return DefaultOrderMethod(entities);
}
}

The second type of the expression passed into the OrderBy extension method is inferred from the type that is returned by the expression. It's expecting a Expression>. So if you're going to store the sort expression you need to explicitly give it the TOrderBy type.
public MyRepository<Post>
{
public Expression<Func<Post, DateTime>> DefaultOrder;
public MyRepository()
{
DefaultOrder = p => p.PublishedOn;
}
public IQueryable<Post> All()
{
var entities = new MyDbContext().Set<Post>();
return entities.OrderByDescending(DefaultOrder);
}
}
.NET does not support boxing/unboxing a generic parameter type unless you're using .NET 4.0 and use interfaces via covariance and contravariance which would not work for your example.
Again this is just how the generic system works in .NET. The only reason something like this works...
Query.OrderBy(x => x.PublishedOn)
... is because the TOrderBy type can be inferred from the expression return type (DateTime).

Related

Get Discriminator value from entity type on metadata workspace

Is there a way to get the discriminator value on a given DbContext and entity type using the metadata workspace? I was hoping to find something that works something like this https://romiller.com/2014/04/08/ef6-1-mapping-between-types-tables/.
Usage will be:
public class MyContext : DbContext
{
public DbSet<Foo> Foo { get; set; }
}
public class FooBase
{
}
public class Foo : FooBase
{
}
public void Test()
{
// should be "Foo"
var discriminator = GetDiscriminatorValue(typeof(Foo), new MyContext());
}
public static string GetDiscriminatorValue(Type type, DbContext context)
{
//...
}
I think I was able to solve this using the following method.
public static string GetDiscriminatorValue(this DbContext context, Type type)
{
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
// Get the mapping between CLR types and metadata OSpace
var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));
// Get the entity type from the model that maps to the CLR base type of the given type
var entityType = metadata
.GetItems<EntityType>(DataSpace.OSpace)
.Single(e => objectItemCollection.GetClrType(e) == type.BaseType);
// Get the entity set that uses this entity type
var entitySet = metadata
.GetItems<EntityContainer>(DataSpace.CSpace)
.Single()
.EntitySets
.Single(s => s.ElementType.Name == entityType.Name);
// Find the mapping between conceptual and storage model for this entity set
var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
.Single()
.EntitySetMappings
.Single(s => s.EntitySet == entitySet);
// Find the value condition (discriminator) that the given type is mapped
var discriminator = mapping
.EntityTypeMappings
.Single(e => e.EntityType?.Name == type.Name)
.Fragments
.Single()
.Conditions
.OfType<ValueConditionMapping>()
.Single();
return (string)discriminator.Value;
}

Entity Framework Core: Dynamically build select list with navigational properties

The question is similar to this one, but answer does not provide 2 critical things to me:
I need the code to work over navigational properties
I am trying to build extension method
I want to write queries like this:
this.context.User
.Where(t => t.Id > 10)
.SelectCustom(t => t.Address.Country.Title)
.OrderBy(t => t.DisplayName)
.Skip(10).Take(5);
With answer in provided link I get this far:
public class SelectList<TSource>
{
private List<MemberInfo> members = new List<MemberInfo>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
var member = ((MemberExpression)selector.Body).Member;
members.Add(member);
return this;
}
public Expression<Func<TSource, TResult>> ToDynamicColumns()
{
return this.members.??????????;
}
}
public static IQueryable<T> SelectCustom<T>(this IQueryable<T> query, Expression<Func<TSource, TKey>> FirstAdditional = null)
{
var columns = new SelectList<T>();
columns.Add(t => t.Id);
columns.Add(t => t.DisplayName)
if (FirstAdditional != null)
columns.Add(FirstAdditional);
return query.Select(columns.ToDynamicColumns);
}
Can this be done with EF Core 2.0?
EF will look through lambda invoke operations as if the body of that expression was inlined. So I would recommend leaving the source expressions alone and just generate expressions to invoke them.
Also I would keep the result type simple, and just return each row as an array of object. This should result in less overhead than creating lots of dictionaries. If you do need to access fields by name, you should create a single dictionary to maintain the relationship between names and column numbers.
public class SelectList<TSource>
{
private List<LambdaExpression> members = new List<LambdaExpression>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
members.Add(selector);
return this;
}
public Expression<Func<TSource, TResult>> ToDynamicColumns()
{
var parameter = Expression.Parameter(typeof(TSource), "e");
return Expression.Lambda<Func<TSource, object[]>>(
Expression.NewArrayInit(
typeof(object),
members.Select(m =>
Expression.Convert(Expression.Invoke(m, parameter), typeof(object))
)
),
parameter);
}
}
Though in your case, since you are writing an extension method to only return the same key details and a single additional field, you could probably define a single generic type to hold the results, and avoid any mucking around with Linq expressions at all;
public class UserResult<V>{
public int Id { get; set; }
public string DisplayName { get; set; }
public V Value { get; set; }
}
public static IQueryable<UserResult<V>> SelectCustom<V>(this IQueryable<User> query, Expression<Func<User, V>> ValueGetter)
{
return query.Select(u => new UserResult<V>{
Id = u.Id,
DisplayName = u.DisplayName,
Value = ValueGetter(u)
});
}
Well almost, if c# would just allow you to compile a call of one Expression<Delegate> from within another. Instead we can implement an ExpressionVisitor to unwrap any call to Compile;
public class DontCompile : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
// Inline any lambda arguments that are expressions
if (node.Expression is ConstantExpression lambdaArgs
&& node.Member is FieldInfo field
&& typeof(Expression).IsAssignableFrom(field.FieldType))
return (Expression)field.GetValue(lambdaArgs.Value);
return base.VisitMember(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// Don't compile lambda expressions
if (node.Method.Name == "Compile"
&& typeof(LambdaExpression).IsAssignableFrom(node.Object.Type))
return Visit(node.Object);
return base.VisitMethodCall(node);
}
public static Expression<T> Tidy<T>(Expression<T> func) => (Expression<T>)new DontCompile().Visit(func);
}
...
return query.Select(DontCompile.Tidy<...>(u => new UserResult<V>{
Id = u.Id,
DisplayName = u.DisplayName,
Value = ValueGetter.Compile()(u)
});
...
But that's starting to look a bit messy.
You could do this with Expression.ListInit, here the TResult must have an Add instance method e.g. Dictionary<string, object>. This could just work but I havent even compiled it. In any way this should give enough hint on how to build this the way you want.
public Expression<Func<TSource, Dictionary<string, object>>> ToDynamicColumns()
{
var addMethod = typeof(TResult).GetMethod("Add");
var paramX = Expression.Parameter(typeof(TSource), "e");
var bindings =
this.members.Select (
member =>
return Expression.ElementInit(
addMethod,
Expression.Constant(mem.Name),
Expression.Convert(Expression.Property(paramX, member), typeof(object))
);
)
var listInit = Expression.ListInit(
Expression.New(typeof(TResult)),
bindings
);
return Expression.Lambda<Func<TSource, Dictionary<string, object>>(
listInit,
paramX
);
}

How to use AutoMapper to map an Enum Id to a queryable projection based on Enum Values?

I am trying to map an Enum value (Int16) to an IQueryable projection so that the string representation of the Enum value is used rather than the integer value for the database sorting.
The approach I used was taken from here.
Enum as follows:
public enum SafetyCategoryEnum : Int16 { High, Medium, Low }
See my AutoMapper mapping below:
config.CreateMap<SupplierContractEntity, SupplierContractEntityWrapper>()
.ForMember(dest => dest.SafetyCategoryEnumId,
opt => { opt.MapFrom(EnumerableExpressionHelper.CreateEnumToStringExpression((SupplierContractEntity e) => e.SafetyCategoryEnumId)); })
SupplierContractEntity is the EntityFramework Entity.
public class SupplierContractEntity : Entity
{
//Other properties removed for brevity
public Int16 SafetyCategoryEnumId { get; set; }
}
SupplierContractEntityWrapper is a custom business object:
public class SupplierContractEntityWrapper : EntityWrapper
{
//Other properties removed for brevity
public SafetyCategoryEnum? SafetyCategoryEnumId { get; set; }
}
Expression mapping is reversed in AutoMapper which is why the Entity maps to the business object
Implementation of CreateEnumToStringExpression:
public static class EnumerableExpressionHelper
{
public static Expression<Func<TSource, string>> CreateEnumToStringExpression<TSource, TMember>(Expression<Func<TSource, TMember>> memberAccess, string defaultValue = "")
{
var type = typeof(SafetyCategoryEnum);
var enumNames = Enum.GetNames(type);
var enumValues = (Int16[])Enum.GetValues(type);
var inner = (Expression)Expression.Constant(defaultValue);
var parameter = memberAccess.Parameters[0];
for (int i = 0; i < enumValues.Length; i++)
{
inner = Expression.Condition(
Expression.Equal(memberAccess.Body, Expression.Constant(enumValues[i])),
Expression.Constant(enumNames[i]), inner);
}
MyExpressionVisitor myExpressionVisitor = new MyExpressionVisitor();
var expression = Expression.Lambda<Func<TSource, string>>(inner, parameter);
myExpressionVisitor.Visit(expression);
return expression;
}
}
When performing a sort AutoMapper throws the following exception:
InvalidOperationException: Rewriting child expression from type 'System.Nullable`1[SafetyCategoryEnum]' to type 'System.String' is not allowed, because it would change the meaning of the operation.
If this is intentional, override 'VisitUnary' and change it to allow this rewrite.
Is there any way around this type issue?
Any help would be greatly appreciated!
This works for me with the latest AM, but you need to make the destination a string. I think you're not on the latest version and you've hit an already fixed bug.

EF get dbset name in runtime from Type

Purpose:
I need to get the name of the dbset name of the entity
typeof(UserAccount) = "UserAccounts".
But in runtime I need a common type for the loop and therefor do not know example "UserAccount".
Only the "name" from typeof?
I have created an DbContext with some entities.
I have been googling for some time but it do not seem to be working for me because of the Type converting?
Please see my method GetDbSetName in the bottom of this description.
I am pretty new at this EF stuff - so please help med with my issue as described below ;-)
public class MyEntities : DbContext
{
public DbSet<UserAccount> UserAccounts { get; set;}
public DbSet<UserRole> UserRoles { get; set; }
public DbSet<UserAccountRole> UserAccountRoles { get; set; }
}
Defined a list of Type to control the output:
public static List<Type> ModelListSorted()
{
List<Type> modelListSorted = new List<Type>();
modelListSorted.Add(typeof(UserRole));
modelListSorted.Add(typeof(UserAccountRole));
modelListSorted.Add(typeof(UserAccount));
return modelListSorted;
}
The problem is below using Type - If I use "UserAccount" it Works and I get "UserAccounts".
But I do not have the "UserAccount" in runtime as I am in a loop with a serie of types.
I do only have the Type list giving the e
public static loopList()
{
List<Type> modelListSorted = ModelListSorted();
foreach (Type currentType in modelListSorted)
{
string s = DataHelper.GetDbSetName(currentType, db);
}
}
HERE IS THE METHOD GIVING ME THE CHALLANGES ;-)
Meaning not compiling.
saying I am missing a assembly?
I know it is pretty pseudo but can this be done smoothly?
public static string GetDbSetName(Type parmType, MyEntities db)
{
string dbsetname = (db as IObjectContextAdapter).ObjectContext.CreateObjectSet<parmType>().EntitySet.Name;
return dbsetname;
}
The challenge here is that two reflection steps are involved, one to invoke the generic CreateObjectSet method and one to get the EntitySet from the result. Here's a way to do this:
First, the method:
string GetObjectSetName(ObjectContext oc, MethodInfo createObjectSetMethodInfo,
Type objectSetType, Type entityType)
{
var objectSet = createObjectSetMethodInfo.MakeGenericMethod(entityType)
.Invoke(oc, null);
var pi = objectSetType.MakeGenericType(entityType).GetProperty("EntitySet");
var entitySet = pi.GetValue(objectSet) as EntitySet;
return entitySet.Name;
}
As you see, I first get the ObjectSet by invoking the MethodInfo representing the generic method CreateObjectSet<T>(). Then I find the PropertyInfo for the EntitySet property of the generic type ObectSet<T>. Finally, I get this property's value and the name of the obtained EntitySet.
To do this, I first get a MethodInfo for CreateObjectSet<>() (the one without parameters) and the ObjectSet<> type
var createObjectSetMethodInfo =
typeof(ObjectContext).GetMethods()
.Single(i => i.Name == "CreateObjectSet"
&& !i.GetParameters().Any());
var objectSetType = Assembly.GetAssembly(typeof(ObjectContext))
.GetTypes()
.Single(t => t.Name == "ObjectSet`1");
In GetObjectSetName their generic parameters are specified by a concrete entity type, which is done by these "MakeGeneric..." methods.
var oc = (dbContextInstance as IObjectContextAdapter).ObjectContext;
var entityType = typeof(UserRole);
var name = GetObjectSetName(oc, createObjectSetMethodInfo, objectSetType, entityType);
In EF 6 these should be the usings:
using System.Data.Entity.Core.Metadata.Edm
using System.Data.Entity.Core.Objects
using System.Data.Entity.Infrastructure
using System.Linq
using System.Reflection

Entity Framework, LINQ and Generics

I have the following code:
public interface IKeyed<TKey>
{
TKey Id { get; }
}
// This is the entity framework generated model. I have added the
// IKeyed<Guid> interface
public partial class Person : IKeyed<Guid>
{
public Guid Id { get; set; }
}
public class Repository<TKey, TEntity> : IKeyedRepository<TKey, TEntity>
where TEntity : class, IKeyed<TKey>
{
private readonly IObjectSet<TEntity> _objectSet;
public Repository(IOjectSet<TEntity> objectSet)
{
_objectSet = objectSet;
}
public TEntity FindBy(TKey id)
{
return _objectSet.FirstOrDefault(x => x.Id.Equals(id));
}
}
[Update]
Here is how I am calling this:
Db2Entities context = new Db2Entities(_connectionString); // This is the EF context
IObjectSet<Person> objectSet = context.CreateObjectSet<Person>();
IKeyedRepository<Guid, Person> repo = new Repository<Guid, Person>(objectSet);
Guid id = Guid.NewGuid();
Person person = repo.FindBy(id); // This throws the exception.
The above code compiles. When the 'FindBy' method is executed, I get the following error:
Unable to create a constant value of type 'Closure type'. Only primitive types (for instance Int32, String and Guid) are supported in this context.
Since the type of my 'Id' is a Guid (one of the primitive types supported) it seems like I should be able to massage this into working.
Anyone know if this is possible?
Thanks,
Bob
It doesn't work this way. You cannot call Equals because EF doesn't know how to translate it to SQL. When you pass expression to FirstOrDefault it must be always only code which can be translated to SQL. It is probably possible to solve your problem with some manual building of expression tree but I can reference other solutions already discussed on Stack Overflow.
ObjectContext offers method named GetObjectByKey which is exactly what you are trying to do. The problem is that it requires EntityKey as parameter. Here are two answers which show how to use this method and how to get EntityKey:
Entity Framework Simple Generic GetByID but has differents PK Name
generic GetById for complex PK
In your case the code will be less complicated because you know the name of the key property so you generally need only something like this:
public virtual TEntity FindBy(TKey id)
{
// Build entity key
var entityKey = new EntityKey(_entitySetName, "Id", key);
// Query first current state manager and if entity is not found query database!!!
return (TEntity)Context.GetObjectByKey(entityKey);
}
The problem here is that you cannot get entitySetName from IObjectSet so you must either pass it to repository constructor or you must pass ObjectSet.
Just in case you will want to use DbContext API (EFv4.1) in the future instead of ObjectContext API it will be much simplified because DbSet offers Find method:
generic repository EF4 CTP5 getById