I'm working with Entity Framework (v4) under Visual Studio 2010 (going against an Oracle database).
A record has been read in as follows:
MYTABLE_DEF t;
t = db.MYTABLE_DEF.Find(identifier);
Now, I know I can access the fields in t directly:
Console.WriteLine(t.SOMEVALUE);
What I would like to be able to do is reference the fields in t, either by doing some sort of 'index' (something like t[0] for first field, t[1] for second field). If that's not possible, then is it possible to bind the field at run time? Something like:
string whichfield = Console.ReadLine();
Console.WriteLine(t[whichfield]) // I know this won't work
I've basically been learning Entity Framework through trial and error (and google) but haven't come across anything sort of indirect reference like that.
Assuming MYTABLE_DEF is an ordinary Entity Class, you should be able to simply reflect over the public fields and return the nth one. Something like this: (untested)
public object this[int index]
{
Type myType = this.GetType();
PropertyInfo[] myProperties =
myType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
object result = myProperties[myIndex].GetValue(myClassInstance, null);
return result;
}
For convenience, I would write this as an extension method on object:
public static Property IndexedProperty(this object obj, int index)
{
Type myType = obj.GetType();
PropertyInfo[] myProperties =
myType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
return myProperties[myIndex];
}
which you can then call thusly:
var theSixthProperty = myObject.IndexedProperty(6);
var valueOfSixthProperty = theSixthProperty.GetValue();
Having it as an Extension Method eliminates the problem of having to write an indexer for every Entity Class.
Related
In specflow there is a class called Table which has a method Table.CreateInstance<T>(). T basically is a class whose properties will get automatically mapped with contents of the table.
I have multiple classes that T needs to be mapped to based on className that gets passed as a string.
Example:
public void ThenTheFollowingFieldsArePopulated(Table table, string className)
{
Type myType = Type.GetType($"abcd.Data.SecType.{className}, abcd.Data");
var obj = Activator.CreateInstance(myType);
var basicOrder = table.CreateInstance<obj>();
}
I am successfully populating both myType and obj. But...
I get a compilation error as:
"obj is a variable but used as a type".
Please could some one help on a fix for this?
I'm developing an .NET Core 3.1 API, and I had a situation where I needed to iterate an object using foreach. To be able to do this, I used Reflection:
var properties = myClass.GetType().GetProperties();
After that, the code goes through the foreach as normal, and then I return the modified properties object to an external API, but it returns a timeout error message, I think it is because the PropertyInfo[] class isn't very appropriate for returning like this, or it's something else, I don't know.
Because of that, I want to convert properties "back" to myClass, or maybe convert it into an dictionary, it would be better to return the original class instead of PropertyInfo[].
How can I convert PropertyInfo[] into a class?
Thanks!
It sounds like you're trying to serialize this class to send to an API. What format does your API accept? If it accepts e.g. json, you should just use a json serializer (like JSON.net - which uses things like reflection under the hood anyways). If you are trying to serialize to a more exotic or unsupported format, then you can do it this way. What's missing is that the Properties[] array doesn't contain the values of your properties, only the definition. You can use the properties array to fetch the values:
public class MyClass
{
public int a { get; set;}
public string b {get; set; }
}
void Main()
{
var instance = new MyClass{a=1,b="2"};
var properties = instance.GetType().GetProperties();
var value_of_a = properties.First(p => p.Name == "a").GetValue(instance);
}
Each property has a "GetValue" method on it that can extract the value corresponding to that property from the class the property came from - note that up above you call 'GetType()' before you call 'GetProperties' - this means you're getting properties for the Type, not the instance. You then need to take those property definitions back to the instance to get the property values.
I'm implementing generics for Add, Remove, and Find, because I'm implementing multi-tenancy that gives me access to IQueryables from the database, as opposed to DbSets.
How Add was implemented:
Code before:
interest = db.Interests.Add(new Interest()) // Interests is dbSet
Code after:
interest = db.Add(new Interest()) // Interests is dbSet
// in db class
public T Add<T>(T item) where T : class, ITenantData
{
var set = _appContext.Set<T>();
return set.Add(item);
}
This works. Now I'm trying to do the same thing for Find, but because I'm working with an Id and not a Typed parameter, I'm a bit stuck.
Code before:
var aptitude = db.Aptitudes.Find(interest.AptitudeId); // Aptitudes is a DbSet
Code after:
var aptitude = db.Find(interest.AptitudeId); // how to identify type?
I can't do this:
var aptitude = db.Aptitudes.Find(interest.AptitudeId);
Because Aptitudes is now an IQueryable.
So this is currently failing:
public T Find<T>(T query) where T : class, ITenantData
{
var set = _mentorContext.Set<T>();
return set.Find(query);
}
Because the type isn't identified - I get :
The type 'int' must be a reference type in order to use it as
parameter 'T' in the generic type or method
'App.Context.TenantContext.Find(T)' Service.cs
I'm a bit new to generics, any help here would be much appreciated.
Solve this with extension methods.
public IQueryable<T> Query<T>() where T : class, ITenantData
{
return _mentorContext.Set<T>().AsNoTracking();
}
...meanwhile in another class...
public static MentorContextExtensions
{
public static InterestEntity ByAptitudeId(
this IQueryable<InterestEntity> queryable,
int aptitudeId)
{
return queryable.SingleOrDefault(x => x.AptitudeId == aptitudeId);
}
}
...usage against IQueryable<T>...
db.Query<InterestEntity>().ByAptitudeId(interest.AptitudeId);
The example above returns a SingleOrDefault, but you could just as easily return sets (pre-filtered IQueryables or IEnumerables) using .Where, bools using .Any, etc.
To maintain usage of the .Find (by primary key) method on the IDbSet, you can do this:
public T Find<T>(params object[] keyComponents) where T : class, ITenantData
{
return _mentorContext.Set<T>().Find(keyComponents);
}
...and use it like this...
var aptitude = db.Find<Aptitude>(interest.AptitudeId);
... that said, using .Find may only be faster when you are doing inserts, updates, deletes with the entity, in some kind of mutation code. .Find will always keep the entity attached to the context, which is what you want when you need to mutate the data. However you cannot invoke .AsNoTracking() on it because it returns a single entity. If you only need the entity to read its properties or for display purposes, .AsNoTracking will keep the entire entity graph (the entity and its navigation / collection properties) detached from the context, which may be faster than using .Find. If you later want to use the same entity instance in mutation code, you can attach it to the context at that time.
I'm trying to create an instance of specified Type whatever user wants to have. For a quick illustration of my purpose please see the code below:
static void Main(string[] args)
{
object o = GetInstance(typeof(int));
Console.WriteLine("Created type: {0}", o.GetType().FullName);
}
public static object GetInstance(Type t)
{
Console.WriteLine("Creating instance of {0}", t.FullName);
return Activator.CreateInstance(t);
}
The problem is Activator.CreateInstance() returns object by default. There is also an overload of this method like T Activator.CreateInstance<T>() which is parameterless and returns the type you specify as T.
However, the problem is T should be hard-coded while calling this method and thus should be a fixed value. I am trying to create an instance of desired class and return it as its type.
Right now if you use this method you should write something like:
int i = GetInstance(typeof(int)) as int
I'm trying to reduce this to:
int i = GetInstance(typeof(int))
Is there a way that I can do casting inside the GetInstance and get rid of that as int repetition? By this way, my return type (and also the type I cast the object to) will be unknown at compile time.
Seemed impossible by design to me but I'd really appreciate if you figure it out.
EDIT: Where I'm stuck is e.g. while you're casting, you can do return (T) result if you are in a generic method, but you can't do Type t = ...; return (t) result this doesn't work. You cannot cast to a type which is passed to you as a parameter which is not known at compile time.
Follow a known pattern
This is not a new problem. It is a problem facing any API that allows type-specific return values. For example, a JSON parsing library like Newtonsoft (which is, to wit, the single most popular .NET package downloaded by .NET programmers in 2019) must be able to parse a string and return a type-specific object, which may or may not be known at compile time. It might make sense to follow their example.
Newtonsoft exposes three ways to specify the type when deserializing. You could do as you are currently doing:
//Cast required
var result = JsonConvert.DeserializeObject(text, typeof(MyType)) as MyType;
You can use a generic method:
//No cast required, but you have to hardcode a type as a type parameter
var result = JsonConvert.DeserializeObject<MyType>(text);
Or you can use an instance as a template, which is great for anonymous types, although you can use it with non-anonymous classes as well. This one works via generic type inference:
//No cast required and no need to specify type; the type is inferred from the argument
var result = JsonConvert.DeserializeAnonymousType(text, new MyType());
Here's how you'd do it:
So for you to make this work, your code might look like this:
public object GetInstance(Type type)
{
return Activator.CreateInstance(type);
}
int i = GetInstance(typeof(int)) as int;
public T GetInstance<T>()
{
return Activator.CreateInstance<T>();
}
int i = GetInstance<int>();
public T GetInstance<T>(T template)
{
return Activator.CreateInstance<T>();
}
int i = GetInstance(0);
If you do it this way, it's hard to imagine any programmer would have trouble using your library, as the approach should already be familiar to them.
Actually you could write GetInstance like this:
static T GetInstance<T>()
{
return Activator.CreateInstance<T>();
}
And use it:
int j = GetInstance<int>();
This might help you to create instance of desired type:
public class ConcreteFactory<T> : AbstractFactory<T>
{
public override T CreateInstance(string typeName,params object[] parameters)
{
var path = Assembly.GetExecutingAssembly().CodeBase;
var assembly = Assembly.LoadFrom(path);
var type = assembly.GetTypes().SingleOrDefault(t => t.Name == typeName);
return (T)Activator.CreateInstance(type, parameters);
}
}
Key here is generic type T can be used to cast the created instance, this can be used as a template to create instance of any type with parameterized constructor
I'm using Entity Framework 4 with POCO template.
I have a List where MyObject are dynamic proxies. I want to use the XmlSerializer to serialize this list, but I don't want them serialized as DynamicProxies but as the underlaying POCO object.
I know about ContextOptions.ProxyCreationEnabled, but I do not want to use that. I just want to know how to cast a proxy object to it's underlaying POCO to serialize.
Faced the same issue today and used Value Injecter to solve it. It's as simple as:
var dynamicProxyMember = _repository.FindOne<Member>(m=>m.Id = 1);
var member = new Member().InjectFrom(dynamicProxyMember) as Member;
Ill dig these old bones up by offering a solution that helped me. Hopefully, it will help someone that reads it.
So, there are actually two solutions. If you don't want lazy loading you can always turn off dynamic proxies and that will give you just the entitiy:
public class MyContext : DbContext
{
public MyContext()
{
this.Configuration.ProxyCreationEnabled = false
}
public DbSet<NiceCat> NiceCats {get; set;}
public DbSet<CrazyCat> CrazyCats {get; set;}
public DbSet<MeanCat> MeanCats {get; set;}
}
The other solution is to use the ObjectContext to get the original entity type the proxy stands in for:
using (var db = new MyContext())
{
var meanAssCat = context.MeanCats.Find(CurrentCat.Id)
var entityType = ObjectContext.GetObjectType(meanAssCat.GetType());
}
As you don't want to turn ProxyCreation off, you are stuck DynamicProxy objects wherever you put virtual keyword for object property (EF Context inherits your object and replaces virtual properties with DynamicProxy objects). These DynamicProxy objects do not inherit from your POCO entities, they just have same properties and can be used instead of your POCO. If you really must to convert to POCO object (and I don't believe that someone will come up with a way to cast it), you may try to workaround by writing copy constructor which will copy all properties from passed argument (not very smart from performance standpoint, but what you have to do, you have to do), or maybe using System.Xml.Serialization.XmlTypeAttribute in parent object which contains your dynamic proxy instead of poco to tell serializer how to serialize virtual property (into which type).
Disclaimer: I've created a somewhat generic solution to this problem. I found this old question while searching for a solution so I figured I'd share my solution here to help whoever may be stubbing his or her toe on the same problem.
I ran into the same problem: I needed to get some stuff from Entity Framework, and then use ASP.NET Web Api to serialize it to XML. I've tried disabling lazy loading and proxy creation and using Include(), but on anything but the most basic class hierarchy that led to gigantic SQL queries that took several minutes to execute. I found that using lazy loading and referencing each property recursively was many, many times faster than loading the tree all at once, so I figured I'd need a way to lazy load everything, get it in the form of a POCO, and then serialize it.
I've used this answer by Gert Arnold as the basis for this solution, and then worked from there.
I've created an Unproxy method in the DBContext that takes a (proxied) class instance (something you'd get back from DbContext.Find(id) for instance) and returns that entity as an actual POCO type, with each property, sub-property etc. fully loaded and ready for serialization.
The Unproxy method and some readonly fields:
readonly Type ignoreOnUnproxyAttributeType = typeof(IgnoreOnUnproxyAttribute);
readonly string genericCollectionTypeName = typeof(ICollection<>).Name;
public T UnProxy<T>(T proxyObject) where T : class
{
// Remember the proxyCreationEnabled value
var proxyCreationEnabled = Configuration.ProxyCreationEnabled;
try
{
Configuration.ProxyCreationEnabled = false;
T poco = Entry(proxyObject).CurrentValues.ToObject() as T; // Convert the proxy object to a POCO object. This only populates scalar values and such, so we have to load other properties separately.
// Iterate through all properties in the POCO type
foreach (var property in poco.GetType().GetProperties())
{
// To prevent cycles, like when a child instance refers to its parent and the parent refers to its child, we'll ignore any properties decorated with a custom IgnoreOnUnproxyAttribute.
if (Attribute.IsDefined(property, ignoreOnUnproxyAttributeType))
{
property.SetValue(poco, null);
continue;
}
dynamic proxyPropertyValue = property.GetValue(proxyObject); // Get the property's value from the proxy object
if (proxyPropertyValue != null)
{
// If the property is a collection, get each item in the collection and set the value of the property to a new collection containing those items.
if (property.PropertyType.IsGenericType && property.PropertyType.Name == genericCollectionTypeName)
{
SetCollectionPropertyOnPoco<T>(poco, property, proxyPropertyValue);
}
else
{
// If the property is not a collection, just set the value of the POCO object to the unproxied (if necessary) value of the proxy object's property.
if (proxyPropertyValue != null)
{
// If the type of the property is one of the types in your model, the value needs to be unproxied first. Otherwise, just set the value as is.
var unproxiedValue = (ModelTypeNames.Contains(property.PropertyType.Name)) ? SafeUnproxy(proxyPropertyValue) : proxyPropertyValue;
property.SetValue(poco, unproxiedValue);
}
}
}
}
return poco; // Return the unproxied object
}
finally
{
// Zet ProxyCreationEnabled weer terug naar de oorspronkelijke waarde.
Configuration.ProxyCreationEnabled = proxyCreationEnabled;
}
}
ModelTypeNames is a property I've added to my DBContext that simply returns all the types used in the model. That way we'll know which types we need to unproxy:
private Collection<string> modelTypeNames;
private Collection<string> ModelTypeNames
{
get
{
if (modelTypeNames == null)
{
// We'll figure out all the EF model types by simply returning all the type arguments of every DbSet<> property in the dbContext.
modelTypeNames = new Collection<string>(typeof(VerhaalLokaalDbContext).GetProperties().Where(d => d.PropertyType.Name == typeof(DbSet<>).Name).SelectMany(d => d.PropertyType.GenericTypeArguments).Select(t => t.Name).ToList());
}
return modelTypeNames;
}
}
To deal with ICollection<> properties, we need to first instantiate a new generic collection (I'm using reflection to create a HashSet<> with the right type argument), iterate through all the values, unproxy each value and add it to the new HashSet, which is then used as the value for the POCO's property.
private void SetCollectionPropertyOnPoco<T>(T poco, PropertyInfo property, dynamic proxyPropertyValue) where T : class
{
// Create a HashSet<> with the correct type
var genericTypeArguments = ((System.Type)(proxyPropertyValue.GetType())).GenericTypeArguments;
var hashSetType = typeof(System.Collections.Generic.HashSet<>).MakeGenericType(genericTypeArguments);
var hashSet = Activator.CreateInstance(hashSetType);
// Iterate through each item in the collection, unproxy it, and add it to the hashset.
foreach (var item in proxyPropertyValue)
{
object unproxiedValue = SafeUnproxy(item);
hashSetType.GetMethod("Add").Invoke(hashSet, new[] { unproxiedValue }); // Add the unproxied value to the new hashset
}
property.SetValue(poco, hashSet); // Set the new hashset as the poco property value.
}
Note that I'm calling SafeUnproxy rather than Unproxy. This is because of a weird issue with type inference. Usually when you pass a proxy object to Unproxy(), type inference will infer that T is the POCO type you actually want, not the type of the dataproxy (the one that looks like YourModelPocoType_D0339E043A5559D04303M3033 etc). However, occasionally it does infer T as the dataproxy type, which blows up the
T poco = Entry(proxyObject).CurrentValues.ToObject() as T;
line, because the poco object can't be cast to the proxy type, causing the as operator to return null. To fix this, SafeUnproxy calls the Unproxy method with an explicit type parameter rather than relying on inference: it checks the type of the parameter you pass it, and if the namespace is System.Data.Entity.DynamicProxies, it'll use the type's BaseType (which in the case of a dynamicproxy type is the corresponding POCO type) as the generic type argument.
private object SafeUnproxy(dynamic item)
{
// ProxyCreation is off, so any reference or collection properties may not yet be loaded. We need to make sure we explicitly load each property from the db first.
ExplicitlyLoadMembers(item);
// Figure out the right type to use as the explicit generic type argument
var itemType = item.GetType();
Type requiredPocoType = (itemType.Namespace == "System.Data.Entity.DynamicProxies") ?
itemType.BaseType :
itemType;
// Call Unproxy using an explicit generic type argument
var unproxiedValue = typeof(VerhaalLokaalDbContext).GetMethod("UnProxy").MakeGenericMethod(requiredPocoType).Invoke(this, new[] { item });
return unproxiedValue;
}
Making sure each property is loaded from the database is a matter of iterating through the properties of the object and checking IsLoaded:
private void ExplicitlyLoadMembers(dynamic item)
{
foreach (var property in ((Type)item.GetType()).GetProperties())
{
DbEntityEntry dbEntityEntry = Entry(item);
var dbMemberEntry = dbEntityEntry.Member(property.Name);
// If we're dealing with a Reference or Collection entity, explicitly load the properties if necessary.
if (dbMemberEntry is DbReferenceEntry)
{
if (!dbEntityEntry.Reference(property.Name).IsLoaded)
{
dbEntityEntry.Reference(property.Name).Load();
}
}
else if (dbMemberEntry is DbCollectionEntry)
{
if (!dbEntityEntry.Collection(property.Name).IsLoaded)
{
dbEntityEntry.Collection(property.Name).Load();
}
}
}
}
Finally, the IgnoreOnUnproxyAttribute used to avoid cycles:
[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
sealed class IgnoreOnUnproxyAttribute : Attribute
{
}
Usage is as follows:
MyDbContext db = new MyDbContext();
public Story Get(int storyId)
{
var lazyStory = db.Stories.SingleOrDefault(s => s.Id == storyId);
var unproxied = db.UnProxy(lazyStory);
return unproxied;
}
Performance isn't spectacular due to all the reflection going on, but execution time is on average only slightly (i.e. less than a second) longer than when lazy loading an entity, iterating through all its properties, and then serializing the dynamicproxy itself. Also, it's much, much faster than when using Include() which is dreadfully slow and error-prone.
Hope it helps somebody.
I faced with the same issue in EF 5. I was trying to serialize my entity objects to XML. #Koreyam s answer gave me a hint. I developed it little bit more.
Somewhere in my code i was calling the serializer like this
string objXML = EntitySerializer.Serialize(entity);
Serialize method is generic. So method header is like this :
public static string Serialize<T>(T tObj) where T : class, new()
So in my method body i use value injecter :
T obj = new T().InjectFrom(tObj) as T;
it just solved my issue for all of my entitites.