I have a method signature like this:
IPostprocessComposer<T> Without<TProperty>(Expression<Func<T, TProperty>> propertyPicker)
Example usage:
AutoFixture.Build<Request>()
.Without(p => p.ID)
.Create();
I want to create a more flexible solution and always ignore a few properties from the type (in this case "Request") identified via reflection
My (not working) solution:
IPostprocessComposer<T> row = fixture.Build<T>();
var primitivePropertiesToIgnore = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.Name.ToLowerInvariant().StartsWith("id") || p.Name.ToLowerInvariant().EndsWith("id"));
foreach (var primitiveProperty in primitivePropertiesToIgnore)
{
var x = Expression.Parameter(typeof(T));
var expr = Expression.Lambda(Expression.PropertyOrField(x, primitiveProperty.Name), x);
// This line is working, but of course I need "int" to be a generic type here... how can i achieve this?
row = row.Without((Expression<Func<T, int>>)expr);
}
Question:
I guess the expression must be somehow differently passed to the Without method in order to work? But how?
The simplest way to make this work is to use dynamic:
row = row.Without((dynamic)expr);
Since row is already statically typed, the dynamic behavior will be contained to only this statement.
If you didn't want to use dynamic, you would need to use reflection to invoke the Without method with the right type parameters, but I don't see any advantage in doing that (it's more complicated, but it isn't any more type-safe).
Related
I am using the ORM linq2db.
We have the need to set the column names at runtime.
That's why I can't use the attributes in the POCO-Class and instead use the Fluent Mapping Api (which is not documented as far as I know).
Because the column names will be determined at runtime I want to iterate over all properties in my Entities and set the Columnname.
In EF Core 3.x this would look like something like this:
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties()))
{
property.SetColumnname(columnNameDict[property.Name]);
}
Is there a possibility to iterate over all the Properties in my Model, like in EF Core?
In linq2db to set the properties of a Property I have to use code like this:
builder
.Entity<Addresses>()
.HasTableName("ADDRESSES")
.Property(x => x.Company) // This is the Membergetter, which i can't use in a foreach afaik.
.HasColumnName("TEXT1")
To Access the Property ID I have to use "x => x.ID"(Expression<Func<Addresses, object>> memberGetter), but when I Iterate over the Properties, I don't know how to select the correct Property (PropertyMappingBuilder) without using this Expression (memberGetter) or how to set this Expression in a foreach. Is there a string overload like this .Property("Company")? I coudn't find any.
I tried it with reflection, but the Expression part didn't work ;):
foreach (var property in typeof(Addresses).GetProperties())
{
var fieldInfo = adressenInfo.GetFieldByUser(property.Name);
adressenBuilder.Property(a => property.GetValue(a)) // this line doesn't work
.HasColumnName(fieldInfo.LogicalName)
.HasDbType(fieldInfo.DataBaseType);
}
Any Ideas are welcome! Thanks :)
You need to generate proper LambdaExpression manually for Parameter() method.
var param = Expression.Parameter(typeof(Addresses));
adressenBuilder.Property(Expression.Lambda<Func<Addresses, object>>(
Expression.Convert(Expression.Field(param, fieldInfo), typeof(object)),
param))...
I want to make a factory class to return DbContext object where table name will be passed as string. There are more than 100 tables in database, each having a different structure/schema.
The idea is to pass tableName as string in the method which will return object in EF and we can select/update these records. I found this code in some article but have some confusion how to use it:
public ObjectContext Context(EntityObject entity)
{
var relationshipManager = ((IEntityWithRelationships)entity).RelationshipManager;
var wrappedOwnerProperty = relationshipManager.GetType().GetProperty("WrappedOwner", BindingFlags.Instance | BindingFlags.NonPublic);
var wrappedOwner = wrappedOwnerProperty.GetValue(relationshipManager);
var contextProperty = wrappedOwner.GetType().GetProperty("Context");
return (ObjectContext)contextProperty.GetValue(wrappedOwner);
}
I am not sure if this is what I need. Also what should I pass in EntityObject entity and where I should pass the tableName?
Please let me know if there is any other way to achieve the same thing.
One simple way to do this is to use the DbContext.Set() method, and get the type based on the string.
using (var db = new MyDbContext)
{
string tableName = "ApplicationUser";
var type = Assembly.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(t => t.Name == tableName);
if(type != null)
DbSet catContext = context.Set(type);
}
However, this has one drawback and that's that this is a non-generic DbSet (ie it's a DbSet not a DbSet<T>).
A way to get the Generic DbSet (which allows linq functionality) would be to do something like this:
using (var db = new IdentityDbContext())
{
string tableName = "ApplicationUser";
var type = Assembly.GetExecutingAssembly()
.GetTypes().FirstOrDefault(t => t.Name == tableName);
var method = db.GetType().GetMethods()
.First(x => x.IsGenericMethod && x.Name == "Set");
MethodInfo generic = method.MakeGenericMethod(type);
var set = generic.Invoke(db, null);
}
Of course set will be an object here, and you'll have to cast it somehow, which is still part of the problem.
When it boils down to it, if you aren't going to work with statically compiled types, you're going to have to keep dealing with reflection, particularly when you have generic types to deal with (ie DbSet<T>, among others). You have to get them cast to a static type at some point to call the methods, or keep doing MethodInfo.Invoke's.
Another option is to use dynamic, but you can't use dynamics with c# extension methods (without casting to a concrete type) so you're back in the same boat of no Linq support.
Using Linq by reflection is a huge pain.
To be honest, If you have 100 classes, I'd just bite the bullet and write the hard coded types, or use a code generator to do it for you like CodeSmith.
I have developed a MVC helper for generating display and editable tables (a jquery plugin is required to allow dynamic addition and deletion of rows with full postback in the editable tables) e.g.
#Htm.TableDisplayFor(m => m.MyCollection as ICollection)
which used in conjunction with attributes will include totals in the footer, add columns for view and edit links, render hyperlinks for complex type etc. e.g.
[TableColumn(IncludeTotals = true)]
I'm about to publish it on CodeProject but before doing so, would like to solve one issue.
The helper first gets the ModelMetadata from the expression, checks that it implements ICollection, then gets the type in the collection (note the following snippet is from accepted answers on SO, but as explained below, is not entirely correct)
if (collection.GetType().IsGenericType)
{
Type type = collection.GetType().GetGenericArguments()[0]
The type is used to generate ModelMetadata for the table header (there might not be any rows in the table) and each row in the table body (in case some items are inherited types which have additional properties and would otherwise screw up the column layout)
foreach (var item in collection)
{
ModelMetadata itemMetadata = ModelMetadataProviders.Current
.GetMetadataForType(() => item, type);
What I would like to be able to do is use IEnumerable rather than ICollection so that .ToList() does not need to be called on linq expressions.
In most cases IEnumerable works fine e.g.
IEnumerable items = MyCollection.Where(i => i....);
is OK because .GetGenericArguments() returns an array containing only one type.
The problem is that '.GetGenericArguments()' on some queries returns 2 or more types and there seems to be no logical order. For example
IEnumerable items = MyCollection.OrderBy(i => i...);
returns [0] the type in the collection, and [1] the type used for ordering.
In this case .GetGenericArguments()[0] still works, but
MyCollection.Select(i => new AnotherItem()
{
ID = i.ID,
Name = 1.Name
}
returns [0] the type in the original collection and [1] the type of AnotherItem
So .GetGenericArguments()[1] is what I need to render the table for AnotherItem.
My question is, is there a reliable way using conditional statements to get the type I need to render the table?
From my tests so far, using .GetGenericArguments().Last() works in all cases except when using OrderBy() because the sort key is the last type.
A few things I've tried so far include ignoring types that are value types (as will often be the case with OrderBy(), but OrderBy() queries might use a string (which could be checked) or even worse, a class which overloads ==, < and > operators (in which case I would not be able to tell which is the correct type), and I have been unable to find a way to test if the collection implements IOrderedEnumerable.
Solved (using comments posted by Chris Sinclair)
private static Type GetCollectionType(IEnumerable collection)
{
Type type = collection.GetType();
if (type.IsGenericType)
{
Type[] types = type.GetGenericArguments();
if (types.Length == 1)
{
return types[0];
}
else
{
// Could be null if implements two IEnumerable
return type.GetInterfaces().Where(t => t.IsGenericType)
.Where(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.SingleOrDefault().GetGenericArguments()[0];
}
}
else if (collection.GetType().IsArray)
{
return type.GetElementType();
}
// TODO: Who knows, but its probably not suitable to render in a table
return null;
}
I would like to make my code convention-based by using Types and keeping things simple, but generics has it's own complexity with it's own learning curve.
I have a bunch of POCOs (Plain Old CLR Objects) in a List that I'd like to iterate through later in the code.
var models = new List<Type>();
models.Add(typeof(Person));
models.Add(typeof(Company));
Would like to cycle through each list item:
models.ForEach(m =>
{
var label = m.FullName;
// var data = JsonConvert.DeserializeObject<List<typeof(m)>>(""); // doesn't work
var data = JsonConvert.DeserializeObject<List<m>>(""); // doesn't work either
...
}
The issue is that the "m" in the Deserialize line isn't working. What would be the best way to pass that through, i.e. making the 'List<m>' a 'List<T>' that we can use?
To use generics, you really need to know the Type (T) at compile time, you don't - you know it at run time. (Caveat: Its possible with reflection, but theres no need to use it when there's an overload as described below)
There is an overload of DeserializeObject which takes a Type rather than use generics. So your code would be
models.ForEach(m =>
{
var label = m.FullName;
var data = JsonConvert.DeserializeObject("",m);
...
}
However, as you've pointed out in comments you actually need a List<T> not a single T. You'll need a little bit of reflection, just to create the right type to pass to the above DeserializeObject call.
var tList = typeof(List<>); // Type of open List
models.ForEach(m =>
{
var label = m.FullName;
var tConvert = = tList.MakeGenericType(m);
var data = JsonConvert.DeserializeObject("",tConvert);
...
}
The answer to your question is above, but the more I look at it the harder it is to see what you can actually do with data. all you'll ever know about data is that it is an object. You cant cast it to anything - you wont know if its a list of Person or a list of Company.
Perhaps this was an overly contrived example you've used for a real-life problem. If not I forsee you're next problem is what to do with data!!
If you don't know the type at compile time you can do this with Reflection. Consider the following code:
models.ForEach(m =>
{
var mi = JsonConvert.GetType()
.GetMethod("DeserializeObject");
var m = mi.MakeGenericMethod(new[] { m });
// you pass [null] for the object because it's a [static] method
// and you don't have to declare [args] if you can express it simply
// but keep in mind that it's simply an object[]
m.Invoke(null, args);
}
Another solution is to call the generic method using reflection (if there isn't any overload that takes the type as parameter)
models.ForEach(m =>
{
MethodInfo method = typeof(JsonConvert).GetMethod("DeserializeObject");
MethodInfo generic = method.MakeGenericMethod(m);
generic.Invoke(null, "");
}
What I'm trying to do is to pass an entity object to method and return all the names of the properties in it.
I'm using this code to get all the props names :
return classObject.GetType().GetProperties();
The problem is that this code return "EntityKey" and "EntityState" as properties whe I use it with Entity Object.
Is there any way to do it ?
Thanx in advance
You want all direct properties, but not the properties of the base type, which in your case is EntityObject:
var type = classObject.GetType();
//alternatively call out directly: typeof(EntityObject).GetProperties()...
var basePropertyNames = type.BaseType.GetProperties().Select(x => x.Name);
var props = type.GetProperties().Where(p => !basePropertyNames.Contains(p.Name));
This sample assumes there is a base type (which is the case for DB first), refactor when that is not guaranteed.
Edit from #Matt's comment: All of the above is unnecessary, could slap my head for not thinking of this - just use the right binding flags:
return classObject.GetType().GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
It is also possible without reflection:
using (var context = new ModelContainer())
{
// Access CSDL
var container = context.MetadataWorkspace
.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
// Access name of related set exposed on your context
var set = container.BaseEntitySets[context.YourEntitySet.EntitySet.Name];
// Access all properties
var properties = set.ElementType.Members.Select(m => m.Name).ToList();
// Access only keys
var keys = set.ElementType.KeyMembers.Select(m => m.Name).ToList();
}
As you can see you have access to much more then names. The example shows that you can now which property is part of key. If you access Members directly you can know which property is scalar, complex type or navigation property.
All information are already loaded so there is no need for reflection. If you want to use reflection don't forget to use it only once (first time you need it) and then store and reuse received property names. Reflection is slow so using it each time you need names is a bad practice.
I had the same problem. The solution I found was to create an array with the name of the properties to return (I olnly need a few). In your case, since it can be laborious to keep track of all properties, I would filter the properties EntityKey and EntityState and return all the others. The code would be something like this:
public IEnumerable<PropertyInfo> GetProperties()
{
Type t = this.GetType();
return t.GetProperties()
.Where(p => (p.Name != "EntityKey" && p.Name != "EntityState"))
.Select(p => p).ToList();
}
Don't know if there is a better solution, but it would be nice ;) Hope it helps!
As stated by BrokenGlass but beware if you need performance and you want to do this in loops. Reflection is not a fast thing.
If you need performance you may wish to put a virtual method in your base class to retrieve the properties as an array of strings or whatever, and override that in all derived classes. This would be the fastes approach but with more coding.