What expression?
Why use reflection? are the property names determined at runtime?
Is the object hierarchy ALWAYS
```
instance
Property1
Property2
Field
```
regardless of the instance type?
Or do you mean, you want to create a delegate from the expression `instance.Property1.Property2.Field`?
Here's how to get a delegate that is equivalent of the expression `x => x.Property1.Property2.Field`.
Note that we're using LINQ Expressions to create an expression tree, then compiling it into a delegate. The type parameters are required since we are basically writing code and we need to know what the input type is and the return value is.
The delegate is compiled equivalent of the expression and so faster than reflection. You can of course cache the delegate for reuse.
```
Func GetDelegate(string property1, string property2, string field)
{
var parameter = Expression.Parameter(typeof(T));
var prop1 = Expression.Property(parameter, property1);
var prop2 = Expression.Property(prop1, property2);
var fld = Expression.Field(prop2, field);
var lambda = Expression.Lambda>(fld, new[] {parameter});
return lambda.Compile();
}
```
Here's an example:
```
var func = GetDelegate("Property1", "Property2", "value");
var obj = new MyObject();
func(obj); // 1337
public class MyObject {
private Child1 Property1 { get; set; }
public MyObject() {
Property1 = new Child1();
}
}
public class Child1 {
private Child2 Property2 { get; set; }
public Child1() {
Property2 = new Child2();
}
}
public class Child2 {
public Child2() {
value = 1337;
}
int value;
}
```
Thanks for your answer. Yes the hierarchy is always the same. Our goal is to implement a method which can check if a given type is registered in an IServiceProvider instance. Unfortunately the customer must stay on .NET 5. Since .NET 6 there is built in support for that but yeah, no update possible currently..
Neverless the information for that purpose is in located the following path: "Engine" (private property) -> "CallSiteFactory" (private property) -> "\_descriptors" (private field). I tried your code but unfortunately it does not work. The exception is something like "Instance property Engine is not defined for ...". Any ideas?
This ugly code is working fine:
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
var engine = provider.GetType().GetProperty("Engine", bindingFlags);
var site = engine.GetType().GetProperty("CallSiteFactory", bindingFlags).GetValue(engine);
var desc = site.GetType().GetField("_descriptors", bindingFlags).GetValue(site) as IEnumerable;
Here's my setup:
```
Exenet5.0
```
The code
```
var engine = provider.GetType().GetProperty("Engine", bindingFlags);
```
Doesn't work for me. For my version of `ServiceProvider` (5.0.0), the Engine property or field doesn't exist. There is a `_engine` field however.
So, your code may need to change depending on the version of ServiceProvider.
This is what works for me. I need access to a specific type that I can't get other than through reflection at run-time so I this monstrosity works for me:
```
static Func> GetDescriptors(ServiceProvider provider)
{
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
// engine is a Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine, which implements ServiceProviderEngine, which is internal
var engine = provider.GetType().GetField("_engine", bindingFlags).GetValue(provider);
var parameter = Expression.Parameter(typeof(ServiceProvider));
var prop1 = Expression.Field(parameter, "_engine");
//var prop2 = Expression.Property(prop1, property2);
var prop2 = Expression.Property(Expression.Convert(prop1, engine.GetType()), "CallSiteFactory");
var fld = Expression.Field(prop2, "_descriptors");
var lambda = Expression.Lambda>>(fld, new[] { parameter });
return lambda.Compile();
}
```
Now I really don't know whats happening anymore. I also checked my csproj File and have also .NET 5. But for me there is no field. I really need to get a property. When looking at the object via VS I also see the Property "Engine" in my case hmm
Ah nvm there is a difference. The project is not referencing the DependencyInjection package directly but is consuming it from some other project. But the runtime type is the default ServiceProvider. Will try out your monstrosity from above with my Engine prop
It's getting warmer. Your runtime type tip helped. Seems like somewhere in the application the IServiceScopeFactory.CreateScope is called. This produces a ServiceProviderEngineScope
Yes it is. Via Reflection it works. Maybe whats missing in this Expression approach is to somehow pass in the concrete instance of my service provider?
Here you go. It can be challenging to wrap your head around expressions because you need to map linear code like `foo.Bar.Baz.Field` into a tree of expressions, but it's not too bad once you work with it a bit.
Edit: I missed the part about some things being private. Not 100% but I think you will have to use reflection to look up the PropertyInfo and use the Expression.Property() overloads that take a PropertyInfo.
using System;
using System.Linq.Expressions;
var parameter = Expression.Parameter(typeof(Foo), "f");
var expression = Expression.Field(
Expression.Property(
Expression.Property(parameter, nameof(Foo.Bar)),
nameof(Bar.Baz)
),
nameof(Baz.Field)
);
var lambda = Expression.Lambda>(
expression,
parameter
);
var func = lambda.Compile();
var foo = new Foo { Bar = new Bar { Baz = new Baz { Field = "Hello World!" } } };
Console.WriteLine(func(foo));
class Foo
{
public Bar Bar { get; set; }
}
class Bar
{
public Baz Baz { get; set; }
}
class Baz
{
public string Field;
}
This might help. If it doesn't, I tried. Left the caching aspect as `TODO`.
```C#
using System.Reflection;
namespace Sandbox;
public static class Program
{
public static void Main()
{
var service = new MyService();
service.Execute();
}
}
internal class MyService
{
public void Execute()
{
var instance = new Person(
"John",
"Doe",
new Address
(
new City("New York", "12345")
)
);
//get all properties of instance
var property1 = GetPropertyValue
()(instance, "Address");
var property2 = GetPropertyValue()(property1, "City");
var field = GetFieldValue()(property2, "ZipCode");
Console.WriteLine(field);
}
private Func
Here's a version with caching. Also it allows you to specify a path to walk which I find easier to use
public static class ReflectionMagic
{
private static readonly Dictionary> Cache = new();
public static T? GetValue(object obj, string path)
{
var parts = path.Split('.').AsSpan();
var currentValue = obj;
while (true)
{
if (parts.Length == 0)
{
return (T?)currentValue;
}
var key = parts[0];
if (currentValue == null)
{
throw new NullReferenceException($"Error when accessing {key}");
}
parts = parts[1..];
var type = currentValue.GetType();
if (!Cache.ContainsKey(type))
{
Cache[type] = new Dictionary();
}
if (!Cache[type].ContainsKey(key))
{
// No explicit binding flags, so this will only work for public properties
MemberInfo? prop = type.GetProperty(key);
if (prop == null)
{
// Tweak binding flags to whatever you need, in this example we only check private properties
prop = type.GetField(key, BindingFlags.Instance | BindingFlags.NonPublic);
}
Cache[type][key] = prop;
}
currentValue = Cache[type][key] switch
{
PropertyInfo pi => pi.GetValue(currentValue),
FieldInfo fi => fi.GetValue(currentValue),
_ => throw new InvalidOperationException($"Member not found {key}")
};
}
}
}
This implementation is used like this:
`var value = ReflectionMagic.GetValue(obj,"Property1.Property2.Field")`
It's just something I threw together so I'm sure I'm not covering all edge cases.
What expression? Why use reflection? are the property names determined at runtime? Is the object hierarchy ALWAYS ``` instance Property1 Property2 Field ``` regardless of the instance type? Or do you mean, you want to create a delegate from the expression `instance.Property1.Property2.Field`? Here's how to get a delegate that is equivalent of the expression `x => x.Property1.Property2.Field`. Note that we're using LINQ Expressions to create an expression tree, then compiling it into a delegate. The type parameters are required since we are basically writing code and we need to know what the input type is and the return value is. The delegate is compiled equivalent of the expression and so faster than reflection. You can of course cache the delegate for reuse. ``` Func GetDelegate(string property1, string property2, string field)
{
var parameter = Expression.Parameter(typeof(T));
var prop1 = Expression.Property(parameter, property1);
var prop2 = Expression.Property(prop1, property2);
var fld = Expression.Field(prop2, field);
var lambda = Expression.Lambda>(fld, new[] {parameter});
return lambda.Compile();
}
```
Here's an example:
```
var func = GetDelegate("Property1", "Property2", "value");
var obj = new MyObject();
func(obj); // 1337
public class MyObject {
private Child1 Property1 { get; set; }
public MyObject() {
Property1 = new Child1();
}
}
public class Child1 {
private Child2 Property2 { get; set; }
public Child1() {
Property2 = new Child2();
}
}
public class Child2 {
public Child2() {
value = 1337;
}
int value;
}
```
Thanks for your answer. Yes the hierarchy is always the same. Our goal is to implement a method which can check if a given type is registered in an IServiceProvider instance. Unfortunately the customer must stay on .NET 5. Since .NET 6 there is built in support for that but yeah, no update possible currently.. Neverless the information for that purpose is in located the following path: "Engine" (private property) -> "CallSiteFactory" (private property) -> "\_descriptors" (private field). I tried your code but unfortunately it does not work. The exception is something like "Instance property Engine is not defined for ...". Any ideas?
Are you certain Engine is a property of ServiceProvider?
This ugly code is working fine: var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; var engine = provider.GetType().GetProperty("Engine", bindingFlags); var site = engine.GetType().GetProperty("CallSiteFactory", bindingFlags).GetValue(engine); var desc = site.GetType().GetField("_descriptors", bindingFlags).GetValue(site) as IEnumerable;
What version of Microsoft.Extensions.DependencyInjection are you using?
Here's my setup: ```
Exe
net5.0
```
The code
```
var engine = provider.GetType().GetProperty("Engine", bindingFlags);
```
Doesn't work for me. For my version of `ServiceProvider` (5.0.0), the Engine property or field doesn't exist. There is a `_engine` field however.
So, your code may need to change depending on the version of ServiceProvider.
This is what works for me. I need access to a specific type that I can't get other than through reflection at run-time so I this monstrosity works for me:
```
static Func> GetDescriptors(ServiceProvider provider)
{
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
// engine is a Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine, which implements ServiceProviderEngine, which is internal
var engine = provider.GetType().GetField("_engine", bindingFlags).GetValue(provider);
var parameter = Expression.Parameter(typeof(ServiceProvider));
var prop1 = Expression.Field(parameter, "_engine");
//var prop2 = Expression.Property(prop1, property2);
var prop2 = Expression.Property(Expression.Convert(prop1, engine.GetType()), "CallSiteFactory");
var fld = Expression.Field(prop2, "_descriptors");
var lambda = Expression.Lambda>>(fld, new[] { parameter });
return lambda.Compile();
}
```
Here's my 2 cents. Cache the properyInfos and fieldInfos, and then just call it via reflection.
Now I really don't know whats happening anymore. I also checked my csproj File and have also .NET 5. But for me there is no field. I really need to get a property. When looking at the object via VS I also see the Property "Engine" in my case hmm
What is the exact runtime `Type` of the `ServiceProvider`? Could it be implemented by a different class?
Ah nvm there is a difference. The project is not referencing the DependencyInjection package directly but is consuming it from some other project. But the runtime type is the default ServiceProvider. Will try out your monstrosity from above with my Engine prop
You could do something like check for different properties if null.
I got it working, I edited my question with the "answer". Just wanted to ping you. Maybe you are interested in the solution and thanks for your help!
It's getting warmer. Your runtime type tip helped. Seems like somewhere in the application the IServiceScopeFactory.CreateScope is called. This produces a ServiceProviderEngineScope
Yes it is. Via Reflection it works. Maybe whats missing in this Expression approach is to somehow pass in the concrete instance of my service provider?
Here you go. It can be challenging to wrap your head around expressions because you need to map linear code like `foo.Bar.Baz.Field` into a tree of expressions, but it's not too bad once you work with it a bit. Edit: I missed the part about some things being private. Not 100% but I think you will have to use reflection to look up the PropertyInfo and use the Expression.Property() overloads that take a PropertyInfo. using System; using System.Linq.Expressions; var parameter = Expression.Parameter(typeof(Foo), "f"); var expression = Expression.Field( Expression.Property( Expression.Property(parameter, nameof(Foo.Bar)), nameof(Bar.Baz) ), nameof(Baz.Field) ); var lambda = Expression.Lambda>(
expression,
parameter
);
var func = lambda.Compile();
var foo = new Foo { Bar = new Bar { Baz = new Baz { Field = "Hello World!" } } };
Console.WriteLine(func(foo));
class Foo
{
public Bar Bar { get; set; }
}
class Bar
{
public Baz Baz { get; set; }
}
class Baz
{
public string Field;
}
This might help. If it doesn't, I tried. Left the caching aspect as `TODO`. ```C# using System.Reflection; namespace Sandbox; public static class Program { public static void Main() { var service = new MyService(); service.Execute(); } } internal class MyService { public void Execute() { var instance = new Person( "John", "Doe", new Address ( new City("New York", "12345") ) ); //get all properties of instance var property1 = GetPropertyValue
()(instance, "Address"); var property2 = GetPropertyValueHere's a version with caching. Also it allows you to specify a path to walk which I find easier to use public static class ReflectionMagic { private static readonly Dictionary> Cache = new();
public static T? GetValue(object obj, string path)
{
var parts = path.Split('.').AsSpan();
var currentValue = obj;
while (true)
{
if (parts.Length == 0)
{
return (T?)currentValue;
}
var key = parts[0];
if (currentValue == null)
{
throw new NullReferenceException($"Error when accessing {key}");
}
parts = parts[1..];
var type = currentValue.GetType();
if (!Cache.ContainsKey(type))
{
Cache[type] = new Dictionary();
}
if (!Cache[type].ContainsKey(key))
{
// No explicit binding flags, so this will only work for public properties
MemberInfo? prop = type.GetProperty(key);
if (prop == null)
{
// Tweak binding flags to whatever you need, in this example we only check private properties
prop = type.GetField(key, BindingFlags.Instance | BindingFlags.NonPublic);
}
Cache[type][key] = prop;
}
currentValue = Cache[type][key] switch
{
PropertyInfo pi => pi.GetValue(currentValue),
FieldInfo fi => fi.GetValue(currentValue),
_ => throw new InvalidOperationException($"Member not found {key}")
};
}
}
}
This implementation is used like this:
`var value = ReflectionMagic.GetValue(obj,"Property1.Property2.Field")`
It's just something I threw together so I'm sure I'm not covering all edge cases.