Explicit conversion from integer to entity? - c#

Is it possible in Entity Framework to make a custom converter method for converting an integer into an entity through an explicit conversion?
I did some research on this, and I don't know where to start.
Here's an example of what I'm trying to do.
int activeTeacherId = 38;
Teacher activeTeacher = (Teacher)activeTeacherId;
Edit 1 After some quick research, I figured out that I probably need to do something with the EntityObject if I need everything to be truly generic and flexible. However, I'm not sure how.
Edit 2 From my own experience, I managed to create the following code. However, for obvious reasons, I can't get "this" inside a static context.
If I could just somehow get the type of the object that it's being converted into (since it's not always being converted into an EntityObject, but sometimes a Person or a Teacher), then it would theoretically work.
public class EntityObject : System.Data.Objects.DataClasses.EntityObject
{
public static explicit operator EntityObject(int id)
{
var container = ModelContainer.Instance;
var thisType = this.GetType(); //this can't be done from a static context, so how do we retrieve the type that we are converting into?
var containerType = typeof (ModelContainer);
dynamic setProperty = typeof (ModelContainer).GetProperty(thisType.Name + "Set");
ObjectSet<dynamic> set = setProperty.GetValue(container);
return set.FirstOrDefault(o => o.Id == id);
}
}

I finally did it! Based on Peter's suggestions (see his comment on my question), I have created an extension method on "int" which allows me to perform the actions I am looking for.
I'm quite proud of this algorithm since I figured out most of it myself. It involves some serious reflection stuff, dynamic creation of lambda expressions, and the evaluation of them. But it works!
Any suggestions to make it shorter or better would be appreciated.
public static class EntityObjectExtensions
{
private static int Id;
public static T ToEntity<T>(this int id) where T : class
{
lock(typeof(ModelContainer))
{
Id = id;
var container = ModelContainer.Instance;
var thisType = typeof (T);
while (thisType.BaseType != typeof (EntityObject))
{
thisType = thisType.BaseType;
}
var setProperty = typeof (ModelContainer).GetProperty(thisType.Name + "Set");
dynamic set = setProperty.GetValue(container);
var firstOrDefaultMethod =
typeof (Enumerable).GetMethods().FirstOrDefault(m => m.Name == "FirstOrDefault" && m.GetParameters().Count() == 2);
var firstOrDefaultGenericMethod = firstOrDefaultMethod.MakeGenericMethod(thisType);
var lambda = typeof (Func<,>);
var genericLambda = lambda.MakeGenericType(thisType, typeof (bool));
var lambdaParameter = (Func<T, bool>) Delegate.CreateDelegate(genericLambda, typeof(EntityObjectExtensions).GetMethod("Compare", BindingFlags.Static | BindingFlags.NonPublic));
dynamic item = firstOrDefaultGenericMethod.Invoke(null, new object[] {set, lambdaParameter});
return (T) item;
}
}
private static bool Compare(dynamic item)
{
lock (typeof(ModelContainer))
{
return item.Id == Id;
}
}
}

Related

Casting object to generic type in order to use IEnumerable.Where

I'm trying to implement some filtering in my Dynamic Data + EF website and I'm using a QueryExtender with a CustomExpression. In the OnQuerying event of the CustomExpression I have access to e.Query(where e is of type CustomExpressionEventArgs) which is the Query that is used to get the data from the database. In order to filter the data I need to make changes to this query.
Now this query is of type System.Data.Objects.ObjectQuery<T>, where T is only known at runtime. I'm going to assume that whatever type T is, it's going to have a property called GameId. What I need to do is do a simple Where LINQ statement on this collection where I filter by that property.
short GameId = Convert.ToInt16(Session["GlobalGameFilter"]);
Type currentType = e.Query.ElementType;
//ObjectQuery implements IQueryable and I want to cast to ObjectQuery<CurrentType>
MethodInfo method = typeof(Queryable).GetMethod("Cast").MakeGenericMethod(new Type[] { currentType });
var castedQuery = method.Invoke(e.Query, new object[]{e.Query});
This works, now castedQuery is of type ObjectQuery<currentType>
The problem is that in order to use castedQuery.Where() I need to cast it to an IEnumerable<currentType>, I can't just cast it to the non-generic IEnumerable.
I tried calling Where via reflection (from here):
var whereMethods = typeof(System.Linq.Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.Name == "Where");
MethodInfo whereMethod = null;
foreach (var methodInfo in whereMethods)
{
var paramType = methodInfo.GetParameters()[1].ParameterType;
if (paramType.GetGenericArguments().Count() == 2)
{
whereMethod = methodInfo;
}
}
whereMethod = whereMethod.MakeGenericMethod(currentType);
var ret = whereMethod.Invoke(castedQuery, new object[] { castedQuery, BuildEqFuncFor<Placement>("GameId", GameId) });
But ret is always an empty collection.
Edit: You can see that I hardcoded the type in that last line to be placement (just for testing), but I'd need that to be currentType of course.
Edit2: If I try this
foreach(var item in castedQuery as IEnumerable)
{
var itemGameId = item.GetType().InvokeMember("GameId", BindingFlags.GetProperty, null, item, null);
}
it works, itemGameId actually gets the gameId that i need so the problem must be that the predicate is not being used correctly.
Edit 3: Tried implementing a common interface
public interface IGameId
{
short GameIdGetter { get; }
}
public partial class Placement : IGameId
{
public short GameIdGetter
{
get
{
return this.GameId;
}
}
}
e.Query = e.Query.Cast<IGameId>().Where(x => x.GameIdGetter == GameId);
Get an error here when I try to enumerate e.Query : {"Unable to cast the type 'OfferManagementBackOffice.Placement' to type 'OfferManagementBackOffice.IGameId'. LINQ to Entities only supports casting EDM primitive or enumeration types."}

Generics Fun: Where typeof(List<T>) != typeof(List<T>), and using Reflection to get a generic method, with generic parameters

It was just another day with .NET. Until I had to get generic method of a static class with a generic parameter, using reflection for serialization. Doesn't sound so bad. GetRuntimeMethod("x", new[] { type }), as usual should do the trick, or so I thought.
Now, this method keeps returning null for the following variant:
public static Surrogate<T> BuildSurrogate<T>(List<T> collection).
So, a quick copy to LinqPad, and a GetRuntimeMethods run later, it seemed to have all the methods as expected. Naturally, I thought perhaps, something wasn't right with the behavior of GetRuntimeMethod, so, I whipped up a quick extension, GetRuntimeMethodEx that iterates through, and to my surprise, it failed. What? How could that fail. GetRuntimeMethods has the exact the methodInfo I need.
So, I ended up breaking up the extension into parts to understand what exactly is going on, as in the code below. And it turns out (cType != cGivenType) always ended up true.
But a quick inspection, shows they were the same 'apparent' type - List<T>. Now being utterly confused, a diff on the dump of the two typeof(List<T>), and it turns out they were not the same!
The MetadataToken of the two were exactly the same. However the RuntimeTypeHandle were different. The given type had the correct AssemblyQualifiedName, and FullName, belonging to System.Collections.Generic.List``1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089. Great. But oddly enough, the type on the generic method, had both of them as "null"! So, basically, the List<T> is a magical type with no corresponding assembly(!?). It exists, but doesn't. How fascinating!
Here's a quick dump of the diff between the two, that's relevant.
Note the GenericTypeParameters, and IsGenericTypeDefinition - They are the ones which seem to make perfect sense. The oddities aside, now how could one create a Type that matches this type on the MethodInfo? Potentially, the compiler expects a generic type of List<> with the generic parameter T - The only problem is, you can't literally make a generic type with T. The T has to be a type of something, which now invalidates the equality.
private void Main()
{
var type = typeof (List<>);
var m = typeof (Builders).GetRuntimeMethods();
var surrogateBuilder = typeof (Builders)
.GetRuntimeMethodEx("BuildSurrogate", new[] {type});
}
static class Builders
{
public static Surrogate<T> BuildSurrogate<T>(List<T> collection)
{
return new Surrogate<T>
{
Items = collection.ToArray(),
};
}
public class Surrogate<T>
{
public IEnumerable<T> Items;
}
}
public static class ReflectionExtensions
{
public static MethodInfo GetRuntimeMethodEx(
this Type type, string name, params Type[] types)
{
var m = type.GetRuntimeMethods();
var res = (m.Where(t =>
{
var n = name;
return t.Name.Equals(n);
}).FirstOrDefault(t =>
{
var px = t.GetParameters().ToArray();
var currentTypes = px.Select(p => p.ParameterType).ToArray();
if (currentTypes.Length < 1) return false;
for (var i = 0; i < types.Length; i++)
{
var cGivenType = types[i];
for (var j = 0; j < currentTypes.Length; j++)
{
var cType = currentTypes[j];
if (cType != cGivenType) return false;
}
}
return true;
}));
return res;
}
}
That's because your type is a GenericTypeDefinition (List<>) and the one taken from reflecting your class is an actual List<T>.
Your code is not readable, so I wrote my own from scratch. The important part is in TypesMatch method.
public static MethodInfo GetRuntimeMethodEx(
this Type type, string name, params Type[] types)
{
var withMatchingParamTypes =
from m in type.GetRuntimeMethods()
where m.Name == name
let parameterTypes = m.GetParameters().Select(p => p.ParameterType).ToArray()
where parameterTypes.Length == types.Length
let pairs = parameterTypes.Zip(types, (actual, expected) => new {actual, expected})
where pairs.All(x => TypesMatch(x.actual, x.expected))
select m;
return withMatchingParamTypes.FirstOrDefault();
}
private static bool TypesMatch(Type actual, Type expected)
{
if (actual == expected)
return true;
if (actual.IsGenericType && expected.IsGenericTypeDefinition)
return actual.GetGenericTypeDefinition() == expected;
return false;
}
Returns your method, as expected.
You can't create a Type instance that represents List<T> with unknown T. That's what GetGenericTypeDefinition and List<> are for.

Can I evaluate an expression in a way to determine and possibly set a property that is null?

I have a service that takes an object and based on the properties within will perform different actions; with this any of these properties can be null, meaning don't perform this action.
I am trying to create a very simple to use API to do this in cases where some properties can be multiple levels deep, here is an example of the current implementation
service.PerformActions(DataFactory.GetNewData<ActionsInfo> (
data => data.SomeParent = DataFactory.GetNewData<SomeParentInfo>(),
data => data.SomeParent.SomeProperty = "someValue" ));
This is a slightly simplified version and in real cases I some times have to setup multiple parent properties this way in order to set one string property at the bottom.
What I would like to do is adjust the code within the GetNewData method to handle instantiating these properties as needed so that the code could look like this:
service.PerformActions(DataFactory.GetNewData<ActionsInfo> (
data => data.SomeParent.SomeProperty = "someValue" ));
Here is my current code for GetNewData:
public static T GetNewData<T>(params Action<T>[] actions)
{
var data = Activator.CreateInstance<T>();
foreach (var action in actions)
{
try
{
action(data);
}
catch (NullReferenceException)
{
throw new Exception("The property you are attempting to set is within a property that has not been set.");
}
}
return data;
}
My first thought is to change the params array to be Expression<Action<T>>[] actions and somehow get a member expression for any of these parents that are null, which would allow me to use the activator to create an instance. However my experience with the more advanced features of Expression trees is slim at best.
The reason for attempting to make this API as simplistic as possible is that it is a UI testing framework that will eventually be used by non developers.
Edit:
I want to add one further example of the current implementation to hopefully demonstrate that what I'm trying to do will provide for more readable code, yes there is a very slight 'side-effect' if I can pull this off but I would argue it is a helpful one.
ExampleDataFactory.GetNewData<ServicesAndFeaturesInfo>(
x => x.Property1 = ExampleDataFactory.GetNewData<Property1Type>(),
x => x.Property1.Property2 = ExampleDataFactory.GetNewData<Property2Type>(),
x => x.Property1.Property2.Property3 = ExampleDataFactory.GetNewData<Property3Type>(),
x => x.Property1.Property2.Property3.Property4 = true);
Edit 2:
The classes that I'm working with here are generated from Apache Thrift struct definitions and as such I have no control over them to be able to set up some kinda of smart constructor.
After getting an answer on my other question I now have a fully working solution for this, it isn't quite as simple syntax as I was originally aiming for, but it isn't bad.
public static DataBuilder<T> GetNewData<T>() where T : class, new()
{
return new DataBuilder<T>();
}
The DataBuilder Class:
public class DataBuilder<T>
{
public readonly T data;
public DataBuilder()
{
data = Activator.CreateInstance<T>();
}
public DataBuilder(T data)
{
this.data = data;
}
public DataBuilder<T> SetValue<T2>(Expression<Func<T, T2>> expression, T2 value)
{
var mExpr = GetMemberExpression(expression);
var obj = Recurse(mExpr);
var p = (PropertyInfo)mExpr.Member;
p.SetValue(obj, value);
return this;
}
public T Build()
{
return data;
}
public object Recurse(MemberExpression expr)
{
if (expr.Expression.Type != typeof(T))
{
var pExpr = GetMemberExpression(expr.Expression);
var parent = Recurse(pExpr);
var pInfo = (PropertyInfo) pExpr.Member;
var obj = pInfo.GetValue(parent);
if (obj == null)
{
obj = Activator.CreateInstance(pInfo.PropertyType);
pInfo.SetValue(parent, obj);
}
return obj;
}
return data;
}
private static MemberExpression GetMemberExpression(Expression expr)
{
var member = expr as MemberExpression;
var unary = expr as UnaryExpression;
return member ?? (unary != null ? unary.Operand as MemberExpression : null);
}
private static MemberExpression GetMemberExpression<T2>(Expression<Func<T, T2>> expr)
{
return GetMemberExpression(expr.Body);
}
}
The Usage:
ExampleDataFactory.GetNewData<ServicesAndFeaturesInfo>()
.SetValue(x=> x.Property1.EnumProperty, EnumType.Own)
.SetValue(x=> x.Property2.Property3.Property4.BoolProperty, true)
.Build();
I think you could use the ExpandoObject or the ElasticObject.
ExpandoObject as far as I know it will get "transformed" into a dictionary ( Properties => Values ).

Creating an expression from the string of a property name?

I am trying to create a query based on some JSON, I currently have the JSON parsed into a set of rules, each rule contains the name of the field, the type of comparison (=, > etc) and the value to compare.
The issue I am having is getting it from that rule, to an IQueryable object, I am guessing I need to use reflection and somehow build the expression tree, but I'm not sure on the right approach...
Assuming I have:
public class Order : BaseEntity
{
public int OrderID{ get; set; }
}
and I have the rule which is:
public class Rule
{
public string field { get; set; }
public Operations op { get; set; }
public string data { get; set; }
}
Running it I get:
field = "OrderID"
op = "eq"
data = "123"
I have the method to parse it with the signature:
public IQueryable<T> FilterObjectSet<T>(IQueryable<T> inputQuery) where T : class
As part of this method I want to do:
inputQuery = inputQuery.Where(o => propertyInfo.Name == rule1.data);
This doesn't work because it basically just generates the sql "OrderID" = "123" which is obviously wrong, I need it to take the column name from inputQuery that has the same name as propertyInfo.Name and build the query that way...
Hope that made sense? Any suggestions?
Edit: I guess what I am asking is to convert a string (Because I can build one pretty simply from the rule) to an expression, maybe using Dynamic LINQ?
Something like this:
public static IQueryable<T> FilterObjectSet<T>(IQueryable<T> inputQuery,
Rule rule) where T : class
{
var par = Expression.Parameter(typeof(T));
var prop = Expression.PropertyOrField(par, rule.field);
var propType = prop.Member.MemberType == System.Reflection.MemberTypes.Field ?
((FieldInfo)prop.Member).FieldType :
((PropertyInfo)prop.Member).PropertyType);
// I convert the data that is a string to the "correct" type here
object data2 = Convert.ChangeType(rule.data,
propType,
CultureInfo.InvariantCulture);
var eq = Expression.Equal(prop, Expression.Constant(data2));
var lambda = Expression.Lambda<Func<T, bool>>(eq, par);
return inputQuery.Where(lambda);
}
If you need some explanation, you can ask. Note that this won't work on types that have special implicit conversions (like a MyString type that has an implicit conversion from string). This because Convert.ChangeType uses only the IConvertible interface.
Null handling for data is perhaps something else that should be handled.
Be aware that I'm not sure the Expression.PropertyOrField is handled by the various IQueryable<T> engines (LINQ-to-SQL and EF). I have only tested it with the AsQueryable() engine. If they don't "accept" it, you must split it in a Expression.Property or Expression.Field depending on what rule.field is.
A nearly equivalent version that doesn't use Expression.PropertyOrField:
public static IQueryable<T> FilterObjectSet<T>(IQueryable<T> inputQuery,
Rule rule) where T : class
{
Type type = typeof(T);
var par = Expression.Parameter(type);
Type fieldPropertyType;
Expression fieldPropertyExpression;
FieldInfo fieldInfo = type.GetField(rule.field);
if (fieldInfo == null)
{
PropertyInfo propertyInfo = type.GetProperty(rule.field);
if (propertyInfo == null)
{
throw new Exception();
}
fieldPropertyType = propertyInfo.PropertyType;
fieldPropertyExpression = Expression.Property(par, propertyInfo);
}
else
{
fieldPropertyType = fieldInfo.FieldType;
fieldPropertyExpression = Expression.Field(par, fieldInfo);
}
object data2 = Convert.ChangeType(rule.data, fieldPropertyType);
var eq = Expression.Equal(fieldPropertyExpression,
Expression.Constant(data2));
var lambda = Expression.Lambda<Func<T, bool>>(eq, par);
return inputQuery.Where(lambda);
}
In the end I used a Dynamic Linq library I found on Guthrie's Blog:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Using this I was able to properly parse out and use the parameters I had built into the rules

Expression of type 'MyEnum' cannot be used for parameter of type

I created Enum ToFrendlyString function for my enums, but i cant use in Linq.
public enum MyEnum
{
Queued = 0,
[Description("In progress")]
In_progress = 2,
[Description("No answer")]
No_answer = 6,
}
public static class EnumToFrendlyString
{
public static string ToFrendlyString(this Enum value)
{
return value.GetEnumDescription();
}
public static string GetEnumDescription(this Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
var attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes.Length > 0)
return attributes[0].Description;
return value.ToString();
}
}
When i try to use this function in Linq, im getting error
var res = collection.AsQueryable().Where(p => p.UserID == UserID).OrderByDescending(p=> p.DateCreated).Select(p => new MyClass
{
Date = p.DateCreated.ToString(),
Status = p.Status.ToFrendlyString(),
}).Take(10).ToList();
If i make another function in same class, like
private string MyStatusToString(MyEnum status)
{
return status.ToFrendlyString();
}
and change my Linq to use this function, then everything works.
Error
Expression of type 'DAL.MyEnum' cannot be used for parameter of type 'System.Enum' of method 'System.String ToFrendlyString(System.Enum)'
I'm not sure you can use Enum as the Type for an extension method like that - try this instead. I've taken the liberty of tidying the code up a bit, feel free to ignore those changes :)
public static class EnumToFrendlyString
{
public static string ToFrendlyString<T>(this T value)
where T : struct
{
return value.GetEnumDescription();
}
public static string GetEnumDescription<T>(this T value)
where T : struct
{
return EnumDescriptionCache<T>.Descriptions[value];
}
private static class EnumDescriptionCache<T>
where T : struct
{
public static Dictionary<T, string> Descriptions =
Enum.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(
value => value,
value => value.GetEnumDescriptionForCache());
}
private static string GetEnumDescriptionForCache<T>(this T value)
where T : struct
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Only use with enums", "value");
}
var descriptionAttribute = typeof(T)
.GetField(value.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast<DescriptionAttribute>()
.FirstOrDefault();
return (descriptionAttribute != null)
? descriptionAttribute.Description
: value.ToString();
}
}
I've added a private, generic class to cache the descriptions for your enum members to avoid lots of runtime use of reflection. It looks a bit odd popping in and out of the class to first cache then retrieve the values, but it should work fine :)
The warning I gave in this answer still applies - the enum value passed to the dictionary isn't validated, so you could crash it by calling ((MyEnum)5367372).ToFrendlyString().
I am not sure but it can be that you didn t add the DAL project yet to your current project(Add reference -> projects in solutins -> Dal). Then it might work. (I had a similar issue once and this was my solution)
It seems the problem is that your collection is IQueryable<T> and the query provider is trying to translate your Select() into a query string.
One way to avoid that is to perform Select() in memory using IEnumerable<T>:
var res = collection.AsQueryable()
.Where(p => p.UserID == UserID)
.OrderByDescending(p=> p.DateCreated)
.Take(10)
.AsEnumerable()
.Select(p => new MyClass
{
Date = p.DateCreated.ToString(),
Status = p.Status.ToFrendlyString(),
})
.ToList();

Categories

Resources