My Problem is this A have a API controler And creating a Single method delete to data from X DbSet properties but the don't have the same Generic paramether. My result is to somehow pass a System.Type to Generic paramether. And my Question is some way to do it?
var table = TableInfo.GetValue(_context) as DbSet<[here i need pass it]>;
I need to do something like (I know this can't work)
var table = TableInfo.GetValue(_context) as DbSet<TableInfo.GetType>;
My full code
[HttpDelete("{name}/{id}")]
[Route("api/Delete")]
public IActionResult Delete(string name = "Items", int id = 2)
{
PropertyInfo TableInfo = GetValueByName(name);
if (TableInfo == null)
return NotFound("Haaaah");
var table = TableInfo.GetValue(_context) as DbSet<[here i need pass it]>;
if (table == null)
return BadRequest();
var prop = table.SingleOrDefault(p => p.Id == id);
if (prop == null)
return NotFound(prop);
table.Remove(prop);
_context.SaveChanges();
return Ok();
}
public PropertyInfo GetValueByName(string name)
{
Type t = _context.GetType();
List<PropertyInfo> list = new List<PropertyInfo>(t.GetProperties());
foreach(PropertyInfo m in list)
{
if (m.Name == name)
return m;
}
return null;
}
For end sorry about my English.
And thanks for all answers :)
var table = TableInfo.GetValue(_context) as DbSet<[here i need pass it]>;
You can't do that, you have no compile time information on what type you need, how do you expect to leverage it before the code is even running?
If you really want compile time type information of table you either know the generic type at compile time or you cover all possible execution paths considering all potential generic types your method must handle (horrendous, don't do that).
Using an interface won't work either. A hypothetical IIdEntity and a cast along the lines table as DbSet<IIdEntity> will never work because:
Type variance is only allowed in interfaces and delegates, DbSet is not an interface.
Even if you use IDbSet<TEntity>, this interface is invariant in TEntity so the following will always fail:
class User: IIdEntity { ... }
object o = someDbEntityOfUser;
var db = o as IDbSet<IIdEntity> //will always be null.
The best options you have with your current setup are:
Keep using reflection; use it to inspect the Id property of the entities.
Use dynamic and simply let the runtime resolve the Id call.
Related
As far as I knew, Object.GetType() should never return null. (related discussion)
Dapper .Query() return private class DapperRow instances to be treated as dynamic objects. I found a strange thing: DapperRow's .GetType() return null.
Here's the sample code to reproduce the problem. Create a C# project, reference Dapper and open a connection to SQL Server (or other database), use .Query() to execute simple select query and retrieve the first row of result. Use GetType() to get the type of result object, the return value is null.
using (SqlConnection cn = new SqlConnection(csSql))
{
var rec = cn.Query("select getdate() as D").Single();
var t = rec.GetType(); // t == null
Console.WriteLine(t.Name); // null reference exception
}
I suspect that dynamic or private type is the cause of null, so I write my class library for test:
namespace Lib
{
public class Blah
{
public static dynamic SecretObject;
static Blah()
{
SecretObject = new PrivateType();
}
}
class PrivateType
{
}
}
In another project, get the dynamic type static field and call GetType():
dynamic obj = Lib.Blah.SecretObject;
Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"
According to the test result, even cast private type as dynamic, I still can get the private type information from GetType(), why DapperRow.GetType() return null?
DapperRow is specifically built and utilized within Dapper to provide highly optimized row returns without reiterating header information. This is to help condense the size of the object and reduce redundant data, making it more efficient.
However, it would appear that the StackExchange team took the meta programming even further than a first glance would indicate.
DapperRow implements the System.Dynamic.IDynamicMetaObjectProvide interface, which requires that the GetMetaObject method be implemented:
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
System.Linq.Expressions.Expression parameter)
{
return new DapperRowMetaObject(parameter,
System.Dynamic.BindingRestrictions.Empty, this);
}
DapperRowMetaObject is a custom implementation of DynamicMetaObject that essentially hijacks and overrides what methods can be invoked against the dynamic type and what those calls should translate to. In this case, calls to anything other than the DapperRow's IDictionary.Item getter or the DapperRow.SetValue will fail since they are always routed to those two calls, but the value will be defaulted to null for any "get" calls where the target property does not exist in the table.
public bool TryGetValue(string name, out object value)
{
var index = table.IndexOfName(name);
if (index < 0)
{ // doesn't exist
value = null;
return false;
}
...
}
At that point, any methods invoked on a null dynamic value will throw a RuntimeBinderException:
RuntimeBinderException: Cannot perform runtime binding on a null
reference
You can easily test this hypothesis by replacing GetType() with another call that will throw the exact same exception:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());
Keep in mind, the underlying type information of any properties on the dynamic object itself can still be accessed directly:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);
I get the error
"Error 4 Cannot convert type 'TExternalEntity' to 'OTIS.Domain.InventoryMgmt.OrderHeader'"
Why not? I'm sure it has something to do with the where statement defining the generic type, but not exactly sure how to get around this.
We can see that we are testing to see if the type is of type OrderHeader, so can't we cast to OrderHeader?
public ActionConfirmation<string> CreateUpdateEntity<TExternalEntity>
(TExternalEntity entity, CompanyPreferencesFinancialsSystemCommon preferences)
where TExternalEntity : class, OTIS.Domain.IEntity, IFinancials, new()
{
//Determine TExternalEntity type (invoice, vendor, customer) to determine which
//mapper class to create. Then convert TExternalEntity to TQuickbooksEntity.
if (entity is InvoiceHeader)
{
var qbInvoice = new InvoiceMapper().ToQuickbooksEntity(entity as InvoiceHeader, preferences);
return CreateUpdateQuickBooksEntity(
qbInvoice,
x => x.Id == entity.FinancialsId,
entity.FinancialsId);
}
if (entity is OrderHeader)
{
var orderHdr = (OrderHeader)entity; <------ ERROR HERE
var qbSalesReceipt = orderHdr.ToQuickBooksEntity(preferences);
return CreateUpdateQuickBooksEntity(
qbInvoice,
x => x.Id == entity.FinancialsId,
entity.FinancialsId);
}
You should probably mention that this is a compile-time error, not a run-time error.
The compiler doesn't know that you are testing the type, so it sees that this cast will potentially fail.
Either of the suggestions in the comments should work for you.
If you use (OrderHeader)(object)entity and the entity is not an OrderHeader (you know it is since you are testing for it first, but humor me), then you would get an error on that line.
If you use entity as OrderHeader and the entity is not an OrderHeader, the orderHdr variable would be set to null and execution would continue.
I also trimmed your example down to the minimum code required to duplicate the issue.
public ActionConfirmation<string> CreateUpdateEntity<TExternalEntity>(TExternalEntity entity)
{
if (entity is OrderHeader)
{
var orderHdr = (OrderHeader)entity; //<------ ERROR HERE
}
return null;
}
I have an entity by getting it from DbEntityEntry.Entity. This returns the Entity Framework proxy for the entity.
How do I access the underlying object as its original type instead of the proxy?
Alternatively I need to dynamically try to cast the proxy to the entity type. Here's a start.
var theEntityType = entityEntry.Entity;
if (
theEntityType.BaseType != null
&& entityType.Namespace == "System.Data.Entity.DynamicProxies"
)
theEntityType = entityType.BaseType;
// Now I need to cast to the correct type
// THIS WON'T WORK BECAUSE `theEntityType` is dynamic.
var entityObject = (theEntityType)entityEntry.Entity;
// My entites also don't implement IConvertible
While working with EF 6 i used the following code to get the underlying POCO entity type from proxy type,
var entityType = ObjectContext.GetObjectType(dbEntitymodifiedEntry.Entity.GetType());
ObjectContext.GetObjectType : Return the POCO from proxy object
reference : https://learn.microsoft.com/en-us/ef/ef6/fundamentals/proxies
First I should say there is no underlying object. A proxy doesn't wrap an entity object (decorator pattern), it derives from it (inheritance). So we can't unwrap the entity, we can only convert a proxy to a base object. Conversion (contrary to casting) always creates a new object.
For this conversion, we can exploit the fact that most of the time, by the way proxies are returned by EF, the compile time type of a proxy is the base type. That is, if a proxy is entered as an argument to a generic method, the generic parameter will be inferred as the base type. This feature allows us to create a method that does what you want:
T UnProxy<T>(DbContext context, T proxyObject) where T : class
{
var proxyCreationEnabled = context.Configuration.ProxyCreationEnabled;
try
{
context.Configuration.ProxyCreationEnabled = false;
T poco = context.Entry(proxyObject).CurrentValues.ToObject() as T;
return poco;
}
finally
{
context.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
}
}
Explanation
The proxy object enters the method. Its type is inferred as the base POCO type. Now we can temporarily turn off ProxyCreationEnabled on the context and copy the proxy object to an object of its base POCO type. This copy action gratefully uses a few EF features.
If you end up needing to do this from a project that does not have access to EF or the DBContext, and you don't know if the type you are referencing is a proxy you can do something like this:
public Type GetType
{
get
{
var thisType = _baseObject.GetType();
if (thisType.Namespace == "System.Data.Entity.DynamicProxies")
return thisType.BaseType;
return thisType;
}
}
Proposed answer has number of problems - for example it doesn't preserve properties defined in partial classes for generated POCO classes and it reloads entity from DB (which also hits the performance).
You can try to turn proxies off before you ask for changes, but it may not help if entities have already been loaded before - they'll be proxy types already (probably it depends on EF version, but it worked out once and didn't work another time in my experience).
You also need to materialize it before you turn proxies back - it isn't obvious but it's just deferred query which has to be materialized:
context.Configuration.ProxyCreationEnabled = false;
var changes = context.ChangeTracker.Entries().ToArray();
To get a JSON friendly object in EF Core I used this method:
T UnProxy<T>(T efObject) where T : new()
{
var type = efObject.GetType();
if (type.Namespace == "Castle.Proxies")
{
var baseType = type.BaseType;
var returnObject = new T();
foreach (var property in baseType.GetProperties())
{
var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
if (propertyType.Namespace == "System")
{
var value = property.GetValue(efObject);
property.SetValue(returnObject, value);
}
}
return returnObject;
}
return efObject;
}
Use AutoMapper 4.2.1 It is having DynamicMap which can remove the Proxy from the object.
var parents = parentsRepo.GetAll().ToList();
Mapper.CreateMap<Parent,ParentDto>();
var parentsDto = Mapper.DynamicMap<List<ParentDto>>(parents);
I have the following generic method inside my class that works as a repository pattern:
public DbSet<T> GetAll<T>() where T : class
{
return dbContext.Set<T>();
}
Now, i would like to get a list of all entities in the database that belong to an entity class that implements a specific interface (IChangeTrackingEntity). So currently there are around 10 specific tables/classes that conform to this, but i don't want to add 10 hardcoded calls to these tables, so I would like to do it using reflection instead (it might also be that the classes that implement this interface change in the future and I don't want to have to remember to change here as well and make the code dependant on each other).
Example of code that works, but that i don't want:
var result = new List<IChangeTrackingEntity>();
using ( var repository = new DbRepository())
{
result.AddRange( repository.GetAll<FirstTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<SecondTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<ThirdTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<FourthTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<FifthTypeThatImplementsInterface>() );
}
return result;
I am quite close, but I can't get the last part to work of casting the result of the Invoke back to the correct type. Waht i got currently is this:
var result = new List<IChangeTrackingEntity>();
var method = typeof (DbRepository).GetMethod("GetAll");
using ( var repository = new DbRepository())
{
foreach (var p in typeof(AnchorDbContext).GetProperties())
{
if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
{
var pType = p.PropertyType.GetGenericArguments()[0];
if (pType.GetInterface("IChangeTrackingEntity") != null)
{
var genericMethod = method.MakeGenericMethod(new[] {pType});
result.AddRange(genericMethod.Invoke(repository, null) as DbSet<IChangeTrackingEntity>);
}
}
}
return result;
}
The problem above it that the Invoke call return a object and I need to cast it to basically DbSet<pType>.
In my current code genericMethod.Invoke(repository, null) as DbSet<IChangeTrackingEntity> returns null indicating that I can't cast the return value as I want, so I need the specific return value type and not the interface I think.
Any idea of how to accomplish this?
Try casting to IEnumerable<IChangeTrackingEntity>. This should work due to co/contravariance.
I don't know much about this specific issue, but you seem to be casting from DbSet<T> where T : IChangeTrackingEntity to DbSet<IChangeTrackingEntity>. This is called covariance or contravariance (I always get confused between them...) and it only works if DbSet<> is an interface. So, casting won't work here. Use an equivalent interface if you can, or make a generic method that accepts DbSet where T: IChangeTrackingEntity and returns DbSet<IChangeTrackingEntity> somehow. I'll try to work out how to do that, and post an answer, if no one has answered before me (unlikely on this site :P)
I'm thinking you need to see this question:
MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);
I have an Entity Framework 4 project that has built up some brute-force searching code that I'd like to reduce to more generic, and more manageable chunks.
One of my Partial Classes, the Run object, contains Navigation Properties (Entity Collections) to other objects (Run.Nodes, Run.Arcs), as well as Scalars (GUID, Version #), and singlular Navigation Properties (Entity Objects - Run.TimeRange).
Run.Nodes is a Base Class collection of NodeBase, with derived classes of NodeTypeA, NodeTypeB, and NodeTypeC.
Using Reflection:
public EntityObject FindDiscriminant<T>(T needle) where T : EntityObject
{
Boolean test = false;
Type sourceType = this.GetType();
String needleString = needle.GetType().BaseType.Name.ToString();
String needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
//If we don't match anything that means that the object itself is a base class, so we need to try again
if (needleStringLookup == null)
{
needleString = needle.GetType().Name.ToString();
needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
}
var needleProperty = Type.GetType(sourceType.FullName).GetProperty(needleStringLookup);
var runValue = needleProperty.GetValue(this, null);
if (runValue.GetType().ToString().Contains("EntityCollection"))
{
foreach (var obj in (runValue as EntityCollection<T>).ToList())
{
test = (obj as T).Discriminant(needle);
if (test == true)
return obj;
}
}
else
{
test = (runValue as EntityObject).Discriminant(needle);
if (test == true)
return (T)runValue;
}
return null;
}
This method works great for EntityCollections (except NodeBase). If I try and look for a node of NodeTypeC in Run.Nodes, runValue will be an EntityCollection of 173 NodeBase objects. But when I try and iterate over it (.ToList()), I get this error:
System.ArgumentNullException was unhandled
Value cannot be null.
Parameter name: source
My workaround is to check to see of the EntityCollection is of type NodeBase, and have an if statement to handle it, and substitute EntityCollection).ToList() for EntityCollection).ToList()
Any suggestions?
An update to my question, for anyone searching this. The code has changed dramatically, and I'm now using Delegates as SearchActions, and have a generic FindSomething routine that uses those delegates instead of having several search routines each using their own type of input.
The things to note are:
The method of detection for determining if my object I pulled with
reflection is an EntityObject or an EntityCollection
I use a private method to iterate over the EntityCollection that I
pass from my generic FindSomething routine. This takes care of the
base-class comparisons
By having the private method to call, I avoid having to use casting on the EntityCollection - this goes away: (runValue as EntityCollection) as well as (obj as T)
I have created a dynamic Object Dictionary when I instantiate our
application - I go through our collection of objects and map objects
and the properties we care about so I don't have to brute force
through an entire object every search
I use dynamic instead of var - I love dynamic! And I no longer cast
before doing a search.
The function is recursive - the SearchAction delegate gets called
again during the iteration code in the IterateThroughEntityCollection
method.
Good? Bad? Comments? Feedback? It works for me, and it's fast.
Here's the revised code:
private EntityObject FindSomething<T>(Run haystack, T needle, SearchAction<T> sa)
{
//First, assume we haven't found anything
Boolean test = false;
//Next, go through all the objects in a run and see if we find anything
//No need to go through Arcs, if the needle is a type of NodeBase, etc.
Type oldRunElementProperty = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Key;
PropertyInfo runValuePropertyToChange = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Value;
dynamic runValue = runValuePropertyToChange.GetValue(haystack, null);
//Check to see if we're dealing with an EntityCollection or an EntityObject. If it is an EntityObject, we can set that value
//directly. If it is a collection, we need to use the generic method
if (runValuePropertyToChange.PropertyType.IsGenericType && runValuePropertyToChange.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
{
EntityObject result = IterateThroughEntityCollection(runValue, needle, sa);
if (result != null) return result;
}
else
{
test = sa(runValue, needle); if (test == true) return runValue;
}
return null;
}
Private EntityCollection iterator.
private EntityObject IterateThroughEntityCollection<T,U>(EntityCollection<T> haystack, U needle, SearchAction<U> sa) where T: EntityObject
{
Boolean test = false;
foreach(dynamic obj in haystack)
{
test = sa(obj, needle);
if (test == true) return obj;
}
return null;
}