DbSet<TEntity>.Find() - Reflection - c#

Im trying to:
...
int id = 5;
//DB is a instance of DbContext
MethodInfo methodFind = DB.GetType().GetMethod("Find");
var resultFind = methodFind.Invoke(entityCol, new object[]{id});
But an Exception is thrown at "Invoke":
"Object of type 'System.Int32' cannot be converted to type 'System.Object[]'."
Is there a way to call DbSet.Find(params object[] keyValues) with reflection?
ps.
Im using it on a CustomAuthorization attribute, i also accept suggestions to improve this part of code. Im trying to check if a certain register of a certain entity (using an attribute) can be edited by a certain user by its common "CompanyId" property, inherited from a "BaseModel" (so all tables got this property)

Try this:
var resultFind = methodFind.Invoke(entityCol, new object[]{new object[]{id}});
The second parameter to Invoke in an object[] which needs to contains objects the map the parameters of the Find method. The first parameter to Find also happens to be an object[] so you need to nest it.

I solved that using this:
var curEntityPI = DB.GetType().GetProperties().Where(pr => pr.Name == entityName).First();
var curEntityType = curEntityPI.PropertyType.GetGenericArguments().First();
var result = DB.Set(curEntityType ).Find(id);

Related

Noob Issue with Activate.CreateInstance

I am working on being able to dynamically invoke an instantiation of a class dynamically at run time.
I have spent the better part of this morning searching Google for an answer but I am pretty green in this world so I am sure the answers make sense, but they do not to me.
public class MSD : IGBRule
{
public MSD(GoverningBodyRulesGet_Result GBRule, int UserID)
{}
The line error and the error are both below
object v = Activator.CreateInstance(Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules"), UserID, GBRules);
System.MissingMethodException: 'Constructor on type 'RulesEngine.Rules.MSD' not found.'
If you want to create an object and pass arguments to the constructor, you will have to provide the arguments in the correct order, matching the order you have specified in your constructor. So in your case, you want to pass the rule before the user id:
var type = Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules");
object v = Activator.CreateInstance(type, GBRules, UserID);
If you pass the constructor arguments directly to the CreateInstance method, you will have to be careful with common types as it is possible that you are accidentally selecting a different overload that doesn’t call the correct constructor. You can avoid that by passing an object array with the arguments you want to pass:
var type = Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules");
object[] args = new object[] { GBRules, UserID };
object v = Activator.CreateInstance(type, args);

C# activate class with <SomeType>, with a body, dynamically, based on a "type" variable

I was looking around here for the answer to this question, and I found a lot of similar questions
Passing just a type as a parameter in C#
X is a variable but used like a type when trying to cast
how to adjust "is a type but is used like a variable"?
How to pass an Object type to Type parameter in C#
Generic List<T> as parameter on method, Initializing a Generic variable from a C# Type Variable
How do I use reflection to call a generic method?
Reflection To Execute Class With Generic Type
but I wasn't able to use any of them to solve my particular issue.
Basically, I have a class (that I don't want to modify) that is activated in a form like so:
var myEvent = new EventListener<KeyboardEvent/*or other type of event*/>((e) => {
///do stuff with event "e"
});
I want to make a function that makes a new event listener "dynamically", meaning based on a particular variable for the "event type" (forget about the function body for now, just assume they all have the same function body), like:
void makeEvent(Type eventType) {
var myEvent = new EventListener<eventType>((e) => {
///do stuff with event "e"
});
}
as many of you will know, as did those people who posted the above questions, that simple doing that will give a "variable used like a type" error, and it won't work, and many of them suggested using "reflection" to get around this, like (from Reflection To Execute Class With Generic Type):
ar instance = Activator.CreateInstance(implementation);
var results = this.GetType()
.GetMethod("CheckForMessages", BindingFlags.NonPublic | BindingFlags.Instance)
.MakeGenericMethod(interfaceUsesType)
.Invoke(this, null) as object[];
if(results.Count() > 0)
instance.GetType()
.GetMethod("DoThis")
.Invoke(instance, new object[] {results});
or (from Initializing a Generic variable from a C# Type Variable):
Animal a = MyFavoriteAnimal();
var contextType = typeof(EsbRepository<>).MakeGenericType(a.GetType());
dynamic context = Activator.CreateInstance(contextType);
context.DoAnimalStuff();
So these answers theoretically make a class definition, but in my case I need to do 2 things, #1: make the actual class of the EventListener, and #2 actually give the class a body (via the lambda expression above), so how can I do that with Activator.CreateInstance ? Or Is there any other way?
Basically I can't use a lambda in the object[], because its not an object, and if I use an Action of some kind that is an object, then I would need to pass in a generic type, and I'm back to where I started, for example I could theoretically do:
var myType = typeof(EventListener<>).MakeGenericType(eventType);
Activator.CreateInstance(myType, new object[] {
new Action<KeyboardEvent>(e => {})
});
which would compile, but then I'm back to where I started in the first place, because "KeyboardEvent" is itself what I need to change, and if I do:
Action<myDynamicTypeVariable>(e=>{})
I get the same "variable is used as type" error...
Isn't there just some kind of way to actually use a variable as a type?
Or is there a way to set the body of a function after the class instance has been formed?
Or how can I pass in a generic function as one of the object[] arguments without having to specify the type of the function and without using lambdas?

generate predictable unique string based on a generic objects content

Story
I'm trying to write a generic method which combines property names, types and content value to generate a unique string for the value held by the object passed.
The idea is to generate a unique SHA3-512 Hash based on the generated string sequence which can be used to compare objects on generic bases by looking at their content.
Example
Let's say we have a class like this ...
class MyClass {
private Int32 Id = 5;
public String Name = "some string";
protected DateTime CreateDate = DateTime.Parse("2017-08-21 15:00:07");
}
... and the mentioned method to generate the unique string
static String GetContentString<T>(T obj) where T : class {
...
}
In theory this should work somewhat like this:
var myObj = new MyClass();
var uniqueContentString = GetContentString(myObj);
Console.WriteLine(uniqueContentString);
>>> Id:Int32:5$Name:String:some string$CreateDate:DateTime:2017-08-21 15:00:07
Problem
I'm having difficulties building the GetContentString Method. This is what I have already:
Object obj = ... // given object
Type type = obj.GetType();
IList<PropertyInfo> propertyInfos = type.GetProperties().Where(x => x.CanRead).ToList(); // Marker #2
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo pi in propertyInfos)
{
sb.Append(pi.Name);
sb.Append(":");
sb.Append(pi.PropertyType.Name);
sb.Append(":");
sb.Append(pi.GetValue(obj) ?? "[ISNULL]"); // Marker #1
sb.Append(":");
}
return sb.ToString();
I tried running the method for a few different types of values like "some string" or 1234 (Int32) and ran into a few issues.
Given a string, the method call throws an exception of type System.Reflection.TargetParameterCountException and the message Parameter count mismatch at #1. I found out that an optional index can be passed to an overloaded version of pi.GetValue(..) which then returns one of the single letters. But how do you know when to stop? If you call an index which doesn't exist it throwns an exception of the type System.Reflection.TargetInvocationException. How do you get the value of a string object using reflection?
Given an integer value, the method call doesn't find any properties at #2. Which brings up the question of how to get the value of an integer object using reflection?
And also some general questions; do you guys think this is a good approach to get a unique string? Is reflection the way to go here? Is it even possible to write a generic solution to this problem?
Without looking at reflection, how about JSON serialization with something that .net framework is able to ?
Reflection isn't something extremely fast and you'll run into issues at the first unhandled exception.
Then, you should do that recursivly if your objects can contains complex properties, wich is not a problem with json serialization !

C# Reflection Invoke - Returns Generic Object { Type } - Need Type

I have a stored procedure call interface that I'm using to handle results from stored procedures with entity (using Translate method to translate the results of our stored procedure into entities that can be tracked and used in EF as normal)
Here's the basic code...
List<object> current = new List<object>();
object item = ((Type)currenttype.Current).GetConstructor(System.Type.EmptyTypes).Invoke(new object[0]);
ObjectContext actualContext = ((IObjectContextAdapter)context).ObjectContext;
string className = "";
EntityContainer container = null;
string setName = "";
className = ((Type)currenttype.Current).ToString();
container = actualContext.MetadataWorkspace.GetEntityContainer(((IObjectContextAdapter)context).ObjectContext.DefaultContainerName, DataSpace.CSpace);
setName = (from meta in container.BaseEntitySets
where meta.ElementType.FullName == className
select meta.Name).FirstOrDefault();
var t = typeof(ObjectContext).GetMethod("Translate", new Type[] { typeof(DbDataReader), typeof(string), typeof(MergeOption) }).MakeGenericMethod(item.GetType()).Invoke(actualContext, new object[] { reader, setName, MergeOption.AppendOnly });
The issue is that I can't do anything with 't' that I want, it's type is listed as
object {System.Data.Entity.Core.Objects.ObjectResult<POCOClass>}. I can't call any of the normal methods that I can normally on the ObjectResult type such as ToArray or ToList.
I need a way to convert it into System.Data.Entity.Core.Objects.ObjectResult<POCOClass>. The difference being that 't' is listed as type object first.
I cannot use any strongly typed casts because the types will change depending on the stored procedure. I've tried using the dynamic keyword instead of var for t and I've also tried using Convert.ChangeType. It never changes from the object base type. dynamic t returns this the following error:
'System.Data.Entity.Core.Objects.ObjectResult<POCOClass>' does not contain a definition for 'ToList'
Thought I know for a fact it does...
To clear up confusion, here's a watch screenshot. The first line is what's being returned, I want it to be like the second (see Type column).
Edit: might be getting closer... I added this:
var listedT = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(item.GetType()).Invoke(null, new object[] { t });
current.AddRange(listedT); // Error here...
listedT becomes a object {System.Collections.Generic.List<ReportCatalog.Models.Catalog_Reports>} and I get the error, cannot convert from object to System.Collections.Generic.IEnumerable<object>.
Since ObjectResult<T> implements also the non-generic IEnumerable interface, cast it to this type and enumerate it.
var e = (IEnumerable)t;
foreach (object o in e) {
//TODO: use o
}

Applying query options on typeless entity

How can I apply the query options on a non clr type. The .ApplyTo method is throwing an error when I'm using it.
Error:
"The query option is not bound to any CLR type. 'ApplyTo' is only
supported with a query option bound to a CLR type."
var x = (EdmCollectionType)Request.GetODataPath().EdmType;
ODataQueryContext queryContext = new ODataQueryContext(Request.GetEdmModel(), x.ElementType.Definition);
ODataQueryOptions queryOptions = new ODataQueryOptions(queryContext, Request);
//codes.....
queryOptions.ApplyTo(Products);
Just as the error said, the query options can't be applied on a non clr type now.
It is because now webapi need the clr type to generate linq expression for query options.
However you can implement the ApplyTo function on a non clr type yourself with the raw values in the query options:
queryOptions.Filter.RawValue
queryOptions.OrderBy.RawValue
...
ODataQueryContext queryContext = new ODataQueryContext(Request.GetEdmModel(), x.ElementType.Definition);
In the above line, we need to explicitly pass the Class Type obj to ODataQueryContext.
Let's assume a type named A which has been created at runtime.
Type a = //Get the type at runtime ;
ODataQueryContext queryContext = new ODataQueryContext(Request.GetEdmModel(), a);
This should resovle the exception.
Now, come to ApplyTo() we can pass an instance of type A to it.
object instance = Activator.CreateInstance(a);
odataQuerySetting.ApplyTo(instance,, new ODataQuerySettings() { });
However, we would like to run ApplyTo() against a list of objects as well as one instance.
Array class could help us to accomplish this. Suppose we need to create 10 instances:
Array values = Array.CreateInstance(a, 10);
for (int i = 0; i < 10; i++)
{
values.SetValue(Activator.CreateInstance(t), i);
}
Needless to say that we can add properties to our instances as we would like to and then pass it to SetValue().
Finally, the ApplyTo() can be executed against our list as follow:
queryOptions.ApplyTo(values.AsQueryable());
Note: if you get the status:406 then one possible cause could be the JsonFormat. use a custom MediaTypeFormatter to resolve that.
I hope that this might help someone.

Categories