C# reflection get implementing property of an interface property - c#

I have an interface that I've defined a custom attribute on a property and I want to retrieve the relevant property from a derived instance of that interface.
public interface ITopicProcessor<T>
{
[TopicKey]
string TopicName { get; }
[OtherAttribute]
string OtherProperty { get; }
void Process(T message);
}
public class MyClassProcessor : ITopicProcessor<MyClass>
{
public string TopicName => "MyTopic";
public string OtherProperty => "Irrelevant";
public void Process(MyClass message)
{
}
}
I can get close with the following - the main issue is that the derived interface type doesn't seem to have the same custom attributes as the generic type definition. I'm pretty sure it's partly due to needing to use the underlying method implementation rather than use the property value directly
// iType is typeof(ITopicProcessor<MyClass>)
// I also have access to the generic type definition if need be - i.e. typeof(ITopicProcessor<>)
Func<Type, string> subscriberTypeToTopicKeySelector = iType =>
{
// Creating an instance via a dependency injection framework
var instance = kernel.Get(iType);
var classType = instance.GetType();
var interfaceMap = classType.GetInterfaceMap(iType);
// interfaceMap.InterfaceMethods contains underlying get_property method, but no custom attributes
var interfaceMethod = interfaceMap.InterfaceMethods
.Where(x => x.HasAttribute<TopicKeyAttribute>())
.ToList();
var classMethodInfo = interfaceMap.TargetMethods[Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod)];
return classMethodInfo.Invoke(instance, BindingFlags.Default, null, null, CultureInfo.CurrentCulture)
.ToString();
};

Implementing an interface is not inheriting from a class. This is why such appributes do not propagate from interface to class. See: bradwilson.typepad.com/blog/2011/08/interface-attributes-class-attributes.html
But there are workarounds: Can a C# class inherit attributes from its interface?

Going off #thehennyy's comment I got something that not only works, but can handle the [TopicKey] attribute being applied to either a property or method. For my needs I only want it to appear once in an interface, but anyone else can extend this solution for their needs
subscriberTypeToTopicKeySelector = iType =>
{
var instance = kernel.Get(iType);
var classType = instance.GetType();
var interfaceMap = classType.GetInterfaceMap(iType);
var iTopicKeyPropertyGetMethods = iType.GetProperties()
.Where(x => x.HasAttribute<TopicKeyAttribute>())
.Select(x => x.GetMethod);
var iTopicKeyMethods = iType.GetMethods()
.Where(x => x.HasAttribute<TopicKeyAttribute>())
.Union(iTopicKeyPropertyGetMethods);
var indexOfInterfaceMethod = Array.IndexOf(interfaceMap.InterfaceMethods, iTopicKeyMethods.Single());
var classMethodInfo = interfaceMap.TargetMethods[indexOfInterfaceMethod];
return classMethodInfo.Invoke(instance, BindingFlags.Default, null, null, CultureInfo.CurrentCulture)
.ToString();
};

Related

passing entity type as parameter in linq

How would I go about passing an entity type as a parameter in linq?
For e.g. The method will receive the entity name value as a string and I would like to pass the entity name to the below linq query. Is it possible to make the linq query generic ?
public ActionResult EntityRecords(string entityTypeName)
{
var entityResults = context.<EntityType>.Tolist();
return View(entityResults);
}
I would like to pass the Entity type as a parameter and return all property values.
Also, is it possible to filter results based the some property?
Assuming your context class is looking like this:
public class MyContext : DbContext
{
public DbSet<Entity1> Entity1 { get; set; }
public DbSet<Entity2> Entity2 { get; set; }
// and so on ...
}
simplest solution is to write method that looks like
private List<object> Selector(string entityTypeName)
{
if (entityTypeName == "Entity1")
return context.Entity1.ToList();
if (entityTypeName == "Entity2")
return context.Entity2.ToList()
// and so on
// you may add a custom message here, like "Unknown type"
throw new Exception();
}
But we don't want to hardcode this stuff, so let create Selector dynamically with Linq.Expressions
Define a Func field within your controller:
private readonly Func<string, List<object>> selector;
Now you can create a factory for this member:
private Func<string, List<object>> SelectByType()
{
var myContext = Expression.Constant(context);
var entityTypeName = Expression.Parameter(typeof(string), "entityTypeName");
var label = Expression.Label(typeof(List<object>));
var body = Expression.Block(typeof(MyContext).GetProperties()
.Where(p => typeof(IQueryable).IsAssignableFrom(p.PropertyType) && p.PropertyType.IsGenericType)
.ToDictionary(
k => Expression.Constant(k.PropertyType.GetGenericArguments().First().Name),
v => Expression.Call(typeof(Enumerable), "ToList", new[] {typeof(object)}, Expression.Property(myContext, v.Name))
)
.Select(kv =>
Expression.IfThen(Expression.Equal(kv.Key, entityTypeName),
Expression.Return(label, kv.Value))
)
.Concat(new Expression[]
{
Expression.Throw(Expression.New(typeof(Exception))),
Expression.Label(label, Expression.Constant(null, typeof(List<object>))),
})
);
var lambda = Expression.Lambda<Func<string, List<object>>>(body, entityTypeName);
return lambda.Compile();
}
and assign Func with it (somewhere in constructor)
selector = SelectByType();
Now you can use it like
public ActionResult EntityRecords(string entityTypeName)
{
var entityResults = selector(entityTypeName);
return View(entityResults);
}
You have two options:
Option 1: You know the entity type at compile time
If you know the entity type at compile time, use a generic method:
public ActionResult EntityRecords<TEntity>()
{
var entityResults = context.Set<TEntity>.ToList();
return View(entityResults);
}
Usage:
public ActionResult UserRecords()
{
return EntityRecords<User>();
}
Option 2: You know the entity type only at runtime
If you actually want to pass the entity type as a string, use the other overload of Set that takes a type:
public ActionResult EntityRecords(string entityType)
{
var type = Type.GetType(entityType);
var entityResults = context.Set(type).ToList();
return View(entityResults);
}
This assumes that entityType is a fully qualified type name including assembly. See this answer for details.
If the entities are all inside the same assembly as the context - or in another well known assembly - you can use this code instead to get the entity type:
var type = context.GetType().Assembly.GetType(entityType);
This allows you to omit the assembly in the string, but it still requires the namespace.
You can achieve what you want even if the context doesn't have DbSet properties (and if it does, that doesn't harm). It is by calling the DbContext.Set<TEntity>() method by reflection:
var nameSpace = "<the full namespace of your entity types here>";
// Get the entity type:
var entType = context.GetType().Assembly.GetType($"{nameSpace}.{entityTypeName}");
// Get the MethodInfo of DbContext.Set<TEntity>():
var setMethod = context.GetType().GetMethods().First(m => m.Name == "Set" && m.IsGenericMethod);
// Now we have DbContext.Set<>(), turn it into DbContext.Set<TEntity>()
var genset = setMethod.MakeGenericMethod(entType);
// Create the DbSet:
var dbSet = genset.Invoke(context, null);
// Call the generic static method Enumerable.ToList<TEntity>() on it:
var listMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(entType);
var entityList = listMethod.Invoke(null, new[] { dbSet });
Now you've got your list of entities.
One remark: To get rid of some performance impact due to reflection you could cache some types and non-generic method infos.
Another remark: I don't think I would recommend this. As said in a comment: this raises a couple of concerns. For example: are you going to allow a client application to get all unfiltered data of any entity table? Whatever it is you're doing: handle with care.
In your example, it looks like you have a controller action that's taking the entity name as a parameter, so you won't be able to make your method generic. But you can use reflection and avoid the use of generics for the most part.
public ActionResult EntityRecords(string entityTypeName)
{
var entityProperty = context.GetType().GetProperty(entityTypeName);
var entityQueryObject = (IQueryable)entityProperty.GetValue(context);
var entityResults = entityQueryObject.Cast<object>().ToList();
return View(entityResults);
}
There are a few things to keep in mind, though:
The assumption is that you've got a property on your context corresponding to the given entityTypeName argument. If entityTypeName is actually the type name instead of the property name, you'll need to do extra work to find the appropriate property.
Your View will have to know what to do with a collection of objects where the type of the objects is not known at compile time. It'll probably have to use reflection to do whatever you intend for it to do.
There may be some security concerns in a method like this. For example, if the user provides "Database" or "Configuration", you could end up exposing information like your connection string, which has nothing to do with the actual entities you've stored.
Also, is it possible to filter results based the some property?
Yes, and it will involve a similar use of reflection and/or dynamic. You could use a library like Dynamic LINQ to pass strings into LINQ-like method overloads (Where, Select, etc.).
public ActionResult EntityRecords(string entityTypeName, FilterOptions options)
{
var entityProperty = context.GetType().GetProperty(entityTypeName);
var entityQueryObject = entityProperty.GetValue(context);
var entityResults = ApplyFiltersAndSuch((IQueryable)entityQueryObject);
return View(entityResults);
}
private IEnumerable<object> ApplyFiltersAndSuch(IQueryable query, FilterOptions options)
{
var dynamicFilterString = BuildDynamicFilterString(options);
return query.Where(dynamicFilterString)
// you can add .OrderBy... etc.
.Cast<object>()
.ToList();
}

Create Moq instances for generic types

I've got an issue setting up a mock (using moq).
We've got a generic repository:
public class Repository<T> : IRepository<T>
where T : EntityBase
{
public Repository(DbSet<T> set)
{
Set = set;
}
[...]
}
And I would like to dynamically create repositories which will be returned by our IRepositoryResolver:
public interface IRepositoryResolver
{
TRepository Resolve<TRepository, TEntity>()
where TRepository : IRepository<TEntity>
where TEntity : EntityBase;
IRepository<TEntity> ResolveFor<TEntity>()
where TEntity : EntityBase;
IRepository<TEntity> ResolveFor<TEntity>(TEntity entity)
where TEntity : EntityBase;
}
For this, I've implemented these mock setup methods:
private void SetupRepositoryResolverMock()
{
var basisDataTypes = Assembly.GetAssembly(typeof(Basisdata))
.GetTypes()
.Where(p => p.IsClass && !p.IsAbstract
&& typeof(Basisdata).IsAssignableFrom(p))
.ToList();
Basisdata[] basisdataInstances = new Basisdata[basisDataTypes.Count];
for (int i = 0; i < basisDataTypes.Count; i++)
{
basisdataInstances[i] = (Basisdata)Activator.CreateInstance(basisDataTypes.ElementAt(i));
}
_repositoryResolverMock = new Mock<IRepositoryResolver>();
foreach (var basisdataInstance in basisdataInstances)
{
Type genericRepository = typeof(Repository<>);
Type constructedRepository = genericRepository.MakeGenericType(basisdataInstance.GetType());
var repositoryInstance = Activator.CreateInstance(constructedRepository, GetQueryableMockDbSet(new[] { basisdataInstance }).Object);
//_repositoryResolverMock
// .Setup(x => x.ResolveFor(basisdataInstance))
// .Returns(() => repositoryInstance);
}
}
private static Mock<DbSet<T>> GetQueryableMockDbSet<T>(ICollection<T> sourceList) where T : EntityBase
{
[...]
}
The data model is: concrete implementations extend abstract Basisdata which extend abstract EntityBase
Now my issue is that the type passed GetQueryableMockDbSet will always return an instance of Mock<DbSet<Basisdata>> instead of a DbSet of a concrete implementation on line var repositoryInstance = Activator.CreateInstance(constructedRepository, GetQueryableMockDbSet(new[] { basisdataInstance }).Object); which obviously leads to an Exception as T does not match for the repository and DBSet.
Question: How can I make GetQueryableMockDbSet return a DBset for the correct type?
Note that I want this to be dynamic and don't know all the entities extending Basisdata.
[Edit] here the test and setup methods:
[SetUp]
public void Setup()
{
SetupServiceMock();
SetupRepositoryResolverMock();
SetupPersonalModuleMock();
_onlineSyncServicePersonal = new OnlineSyncServicePersonal(_serviceMock.Object, _repositoryResolverMock.Object, new[] { _personalModuleMock.Object });
}
[Test]
public void CheckoutTest()
{
// arrange
var checkoutRequest = new CheckoutRequest
{
DienstId = Guid.NewGuid(),
OrganisationId = Guid.NewGuid(),
CheckoutId = Guid.NewGuid(),
LockModuleNames = new[]
{
Constants.ModuleName.BASE,
Constants.ModuleName.PERSONAL
}
};
// act
var checkoutResult = _onlineSyncServicePersonal.Checkout(checkoutRequest);
// assert
}
The problem here is your use of generic methods with implicitly inferred types. Apart from Basisdata[] all other type usages are var and generics with <T> which the compiler all resolves to Basisdata. Since Moq uses the same mechanism to define the type, instead of looking at the type of the passed object, you end up with DbSet<Basisdata>.
You could get around this with a generic mock-builder class which you create with reflection as well. I quickly build this and tested it, tell me if it works for you too:
public class MockCreator
{
public static Mock CreateMock(Type genericType, Type itemType)
{
var typeToMock = genericType.MakeGenericType(itemType);
var creator = typeof(Mock<>).MakeGenericType(typeToMock);
return (Mock)Activator.CreateInstance(creator);
}
}
// Usage
var types = new Type[0]; // Your entity types
var sets = types.Select(t => MockCreator.CreateMock(typeof(DbSet<>), t)).ToList();
// Or in your case
var setMock = MockCreator.CreateMock(typeof(DbSet<>), basisdataInstance.GetType());
Edit: Reduced code to a single static class that can create the Mocks.

Dynamically Access Properties by Type

I'm trying to access a property that is the same type of which is passed into a generic.
Look at the code:
class CustomClass
{
CustomProperty property {get; set;}
}
class CustomProperty
{
}
Main
{
// Create a new instance of my custom class
CustomClass myClass = new CustomClass();
// Create a new instance of another class that is the same type as myClass.property
CustomProperty myProp = new CustomProperty();
// Call the generic method
DynamicallyAccessPropertyOnObject<CustomProperty>(myProp, myClass);
}
private void DynamicallyAccessPropertyOnObject<T>(this T propertyToAccess, CustomClass class)
{
// I want to access the property (In class) that is the same type of that which is passed in the generic (typeof(propertyToAccess))
// TODO: I need help accessing the correct property based on the type passed in
}
If you can't see from the code. Basically I want to be able to pass in a something into a generic and then access the property on a class that is of the same type as the thing that was passed in.
Is there a good way to do this?
If you need clarification let me know...
You can use Reflection, and LINQ:
private static void DynamicallyAccessPropertyOnObject<T>()
{
var customClass = typeof(CustomClass);
var property = customClass
.GetProperties()
.FirstOrDefault(x => x.PropertyType == typeof(T));
}
If you are doing this for CustomClass only, you can remove both parameters.Then you can call it:
DynamicallyAccessPropertyOnObject<CustomProperty>();
If you want to generalize it, use two generic arguments:
private static void DynamicallyAccessPropertyOnObject<T, K>(K targetObj)
{
var targetType = targetObj.GetType();
var property = targetType
.GetProperties()
.FirstOrDefault(x => x.PropertyType == typeof(T));
if(property != null)
{
var value = (T)property.GetValue(targetObj);
}
}
Then call it:
DynamicallyAccessPropertyOnObject<CustomProperty,CustomClass>(myClass);
If there's only one such property you can do:
var prop = typeof(CustomClass).GetProperties().First(p => p.PropertyType == typeof(T));
object value = prop.GetValue(#class, null);
you can set the value with SetValue:
object valueToSet = ...
prop.SetValue(#class, valueToSet);

Generics & static classes. Implement query helper method

Currently we implement a mapping service like this (the service uses automapper, and we make use of the projection feature on it for this part)
// Injected
// IGenericRepository<Entity> entityRepo
var query = this.entityRepo
.FindAll(a => a.Id == someId)
.Take(1);
var result = this.mappingService
.Map<Entity, EntityDto>(query)
.FirstOrDefault();
I'd like to create an extension that would allow me to do the following
var result = this.entityRepo
.FindAll(a => a.Id == someId)
.Take(1).Map<EntityDto>() <--- Entity inferred from repo type
.FirstOrDefault();
My current attempt:
public static class IQueryableExtensions
{
private static IMappingService mappingService;
// will need to be called in app initialization
public static void InitialiseMapper(IMappingService service)
{
mappingService = service;
}
public static IEnumerable<TDto> Map<TAttribute, TDto>(this IQueryable<TAttribute> value)
where TDto : class
where TAttribute : IEntity
{
return mappingService.Map<TAttribute, TDto>(value);
}
}
Thus currently my implementation would look like this.
var result = this.entityRepo
.FindAll(a => a.Id == someId)
.Take(1).Map<Entity,EntityDto>()
.FirstOrDefault();
Questions:
1) How would i go about inferring the entity type from the IQueryable object
2) I realize i cant create a constructor that takes parameters, when creating a static class. Is the way i init the mapper the best/only way?
I tried that with reflection. The constraints are only for demo. If you want to call the reflection code multiple times be sure to cache the final methodinfo.
void Main()
{
var a = new Entity[] {new Entity { name = "a"},new Entity { name = "b"}};
Console.WriteLine(a.Take(1).Map<EntityDto>());
}
public class Entity
{
public string name;
}
public class EntityDto
{
public string dtoname;
}
public static class EntityExtensions
{
public static IEnumerable<U> Map<T,U>(this IEnumerable<T> e) where T: Entity where U: EntityDto, new()
{
foreach(var a in e)
{
yield return new U() { dtoname = a.name };
}
}
public static IEnumerable<U> Map<U>(this IEnumerable<object> e)
{
var method = typeof(EntityExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Map" && m.GetGenericArguments().Length == 2)
.Single();
method = method.MakeGenericMethod(e.GetType().GetGenericArguments()[0], typeof(U));
return method.Invoke(null, new object[] { e}) as IEnumerable<U>;
}
}
1) Currently, you simply can't do that in C#. The type inference is not good enough. You can either specify all type parameters or none of them.
Edit: If you really want the version with a single parameter, you have to delete the second type parameter, type the parameter as non-generic IQueryable and deal with it. One way of doing that would be to determine the generic IQueryable<T> type at runtime. However, this requires reflection. In the case of IQueryable, you can also use the query provider to get around the reflection.
2) You can use a static type constructor.
public static class MyExtensions {
static MyExtensions() {
//initialization goes here
}
}
This type constructor is even called thread-safe. However, if you manage to throw an exception here, you cannot access the MyExtensionsclass!

Using Implicit Type Syntax on Method Return

Not exactly how sure how to title this question, so I hope the title works.
The question is, can I use something similar to implicit type syntax on method calls. For example, this is the implicit type syntax I am referring to:
var x = new Y(){Foo = "Bar", Id = 1};
And I want to do something like this:
var x = myInstance.CreateItem(){Foo = "Bar", Id = 1};
Is there anything in C# that supports something like this? I don't want to do:
x.Foo = "Bar";
x.Id = 1;
...
Please note that CreateItem returns a dynamic type. The CreateItem method and its class cannot be modified.
I would settle for something similar to the With statement in VB.
Thanks in advance.
UPDATE: Attempting Mark Brackett's solution yielded this code:
TaskItem item = outlook.CreateItem(OlItemType.olTaskItem)._((Action<dynamic>)(i =>
{
i.Subject = "New Task";
i.StartDate = DateTime.Now;
i.DueDate = DateTime.Now.AddDays(1);
i.ReminderSet = false;
i.Categories = "#Work";
i.Sensitivity = OlSensitivity.olPrivate;
i.Display = true;
}));
...
public static class Extension
{
public static T _<T>(this T o, System.Action<dynamic> initialize) where T : class
{
initialize(o);
return o;
}
}
The only problem now is that the extension method doesn't seem to be binding to System._ComObject because I get this error: System._ComObject' does not contain a definition for '_'.
It's called an "object initializer", and no - it's not available for return values (or really, anytime other than with a new statement).
Syntax wise, about the closest I can think of would be to use an Action delegate (which requires changes to the Builder):
MyItem CreateItem(Action<MyItem> afterCreate) {
var i = new MyItem();
if (afterCreate != null) afterCreate(i);
return i;
}
var x = Builder.CreateItem(i => { i.Foo = "Bar"; i.Id = 1; });
If you're in a JavaScripty mood for short method names for commonly used functions, and can't change the Builder, I guess an extension method would also work:
public static T _<T>(this T o, Action<T> initialize) where T : class {
initialize(o);
return o;
}
var x = Builder.CreateItem()._(i => { i.Foo = "Bar"; i.Id = 1; });
Here's a twist for you. CreateItem() returns a dynamic type
Contrary to my comment, dynamic does require a few changes. You need to cast to object or the lambda will complain, and then you need to specify dynamic as T or it'll be inferred as object. Or, create your extension method with Action<dynamic> and no type arguments.
object CreateItem() {
return (object)Builder.CreateItem();
}
public static dynamic __(this object o, Action<dynamic> initialize) {
initialize(o);
return o;
}
var x = CreateItem().__(i => { i.Foo = "Bar"; i.Id = 1; });
You could use named arguments to the factory method:
public static Y Builder.CreateItem(string foo = "", int bar = 0)
{
return new Y() {Foo = foo, Bar = bar};
}
// called like so:
var x = Builder.CreateItem(Foo: "Bar", Id: 1);
You can specify 'public properties' in the constructor of the class but you cannot do that with methods
because
This rules only apply to member properties which is not possible with methods
You can not give public modofier in fields in the method so there is no possibility atall
When you do
var a = new MyClass{Foo="",Id=""};
You are defining properties of the class of this constructor MyClass()
You cannot do that with static or instance method

Categories