Checking Whether Class Implements Generic Interface - c#

I'm using the following generic function to determine whether a class implements a specified interface:
private static bool HasFieldType<TEntity, TInterface>()
{
return typeof(TInterface).IsAssignableFrom(typeof(TEntity));
}
This works fine for the majority of the time.
However, I now have an interface which has a generic parameter:
public interface IStatusField<TEnum> where TEnum : System.Enum
{
TEnum Status { get; set; }
}
And this causes the HasFieldType function to break with an unexpected use of unbound generic name error.
Ideally, I want to call the function like:
if (HasFieldType<TEntity, IStatusField<>>())
{
// builder is an EntityTypeBuilder instance
builder.Property("Status")
.HasMaxLength(255)
.HasConversion(new EnumToStringConverter<>());
}
But this won't work as I'm not specifying the generic type for both the IStatusField<> or the EnumToStringConverter<>.
Is there any way around this?
UPDATE
This code forms part of a generic base IEntityTypeConfiguration class as follows:
public abstract class EntityTypeConfiguration<TPrimaryKey, TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : Entity<TPrimaryKey>
{
public void Configure(EntityTypeBuilder<TEntity> builder)
{
builder.HasKey(e => e.Id);
builder.Property(e => e.Id)
.IsRequired()
.HasMaxLength(13)
.HasValueGenerator<PrimaryKeyValueGenerator>();
// Apply the generic interface properties
builder.ApplyInterfaceFields<TPrimaryKey, TEntity>();
// Apply any additional configuration
this.OnConfigure(builder);
}
protected abstract void OnConfigure(EntityTypeBuilder<TEntity> builder);
}
// In an extension class, I have
public static void ApplyInterfaceFields<TPrimaryKey, TEntity>(this EntityTypeBuilder<TEntity> builder) where TEntity : Entity<TPrimaryKey>
{
// Check other implementations (removed for brevity)
// IStatusField implementation
if (HasFieldType<TEntity, IStatusField<>())
{
builder.Property("Status")
.HasMaxLength(255)
.HasConversion(new EnumToStringConverter<>());
}
}
At the point of checking for IStatusField implementation, I know nothing about the generic type specified. I think this may be the bigger problem...

Ok, so I've managed to punch my way around the problem.
It needs a bit of tidying and some error checking but in the crudest form:
private static bool HasFieldType<TEntity>(Type interfaceType)
{
var interfaces = typeof(TEntity).GetTypeInfo().ImplementedInterfaces;
// Iterate through the interfaces
foreach (var intf in interfaces)
{
// Compare interface names
if (intf.FullName.Contains(interfaceType.FullName))
{
return intf.IsAssignableFrom(typeof(TEntity));
}
}
return false;
}
Which now enables this to work:
// IStatusField implementation
if (HasFieldType<TEntity>(typeof(IStatusField<>)))
{
builder.Property("Status")
.HasMaxLength(255)
.HasConversion<string>();
}
I can just use the built-in automatic string-to-enum conversions from EF to do the grunt work.

Instead of trying to wrestle with resolving generic type arguments from nothing, you might consider approaching it from the opposite direction, by getting a list of interfaces implemented by TEntity, filtering it to search for an IStatusField. Once you've located the field, you can get its' generic type arguments and pass those to your EnumToStringConverter:
var statusField = typeof(TEntity)
.GetInterfaces()
.FirstOrDefault(x => x.Name.StartsWith("IStatusField"));
Value given TEntity : IStatusField<ConsoleColor>:
statusField.GenericTypeArguments = [ typeof(System.Color) ]
From there though you're not done; you must still construct an instance of the generic type EnumToStringConverter<System.Color>. This is rather simple and outlined here.
Edit: I realized that because you'd be invoking a constructor, it's not quite the same. Here's how you'd accomplish this:
var statusField = typeof(TEntity)
.GetInterfaces()
.FirstOrDefault(x => x.Name.StartsWith("IStatusField"));
if (statusField != null)
{
var enumType = statusField.GenericTypeArguments[0]; // get the IStatusField<T> value
// get the default constructor after supplying generic type arg
var converterType = typeof(EnumToStringConverter<>)
.MakeGenericType(enumType)
.GetConstructors()[0];
// invoke the constructor. Note the null (optional) param
dynamic converter = converterType.Invoke(new Object[1]);
builder.Property("Status")
.HasMaxLength(255)
.HasConversion(converter);
}

Related

Getting All Generic Types in C# from an Assembly

I need to get a list of generic types of a library to filter them for certain traits.
I'm trying to test if an interface implementation implements the interface contract correctly, and try to list all types that inherit from that interface, but the interface contains a generic parameter.
The issue that I think this approach has is that if a test/assembly does not generate an instance of the generic type, it will not contain a reference to that type.
I have already found:
this does not give me the posibility to look for types, unless I know the type: C# get the the type Generic<T> given T
getting all types from an assembly does not include generic types, I made a test case below that proves this fact.
public class MyClass<T>
{
public T t { get; set; }
public MyClass(T t)
{
this.t = t;
}
}
public class SimpleTest
{
[Fact]
public void TestCreateMyClass()
{
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => p.GetType().FullName.Contains("MyClass")).ToArray();
Assert.NotEmpty(types); // fails
}
}
My current workaround for this is to explicitly list all classes of that type to make a check, but I would prefer some automated way as people are expected to extend this functionality and I want them to be automatically tested.
You can check if your type has an interface which is generic (IsGenericType) and which GetGenericTypeDefinition() is equal to your generic type interface:
interface IGeneric<T> { }
public class MyClass<T> : IGeneric<T> { }
public class MyClassConcrete : IGeneric<int> { }
var assembly = typeof(IGeneric<>).Assembly; // get assembly somehow
var types = assembly.GetTypes()
.Where(t => !t.IsInterface)
.Where(t => t.GetInterfaces().Any(i => i.IsGenericType && typeof(IGeneric<>) == i.GetGenericTypeDefinition()));
Console.WriteLine(string.Join(", ", types)); // prints MyClass`1[T], MyClassConcrete

StructureMap Generic Ctor Named Instance

Update
I solved this problem with the following code, but it's not the solution I'm looking for. This is still an open bounty for a more generic solution. If we ever have a table that isn't an int or string for the key value we'll have to add to this manually to make it work.
c.For(typeof(ILogDifferencesCommand<,>)).Use(typeof(LogDifferencesCommand<,>))
.Ctor<ILogDifferencesLogger<int>>()
.Named(AppSettingsManager.Get("logDifferences:Target"))
.Ctor<string>()
.Named(AppSettingsManager.Get("logDifferences:Target"));
Original Question
I have three types of loggers, and I've defined named instances in my container for them:
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesAllLogger<>))
.Named("all");
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesNLogLogger<>))
.Named("nlog");
c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesDatabaseLogger<>))
.Named("database");
The LogDifferencesCommand receives an ILogDifferencesLogger<> as its only argument:
public LogDifferencesCommand(ILogDifferencesLogger<TKey> logDifferencesLogger)
{
this.logDifferencesLogger = logDifferencesLogger;
}
How do I properly configure the ILogDifferencesCommand<> to grab the right named instance based off of an application setting? Right now I've got something like this:
c.For(typeof(ILogDifferencesCommand<,>))
.Use(typeof(LogDifferencesCommand<,>));
The issue I'm having is I can't pull in the Ctor<> because I can't use unbound generics with that signature, so then I can't use the Named method off the Ctor for that.
For example, I could do something like this, but that wouldn't hit all possible types:
c.For(typeof(ILogDifferencesCommand<,>)).Use(typeof(LogDifferencesCommand<,>))
.Ctor<ILogDifferencesLogger<int>>()
.Named(AppSettingsManager.Get("logDifferences:Target"));
But the problem then is I'd have to handle every TKey type the system uses.
Class and Interface Definitions
public class LogDifferencesCommand<TModel, TKey> : ILogDifferencesCommand<TModel, TKey>
where TModel : class, IIdModel<TKey>
{
public LogDifferencesCommand(ILogDifferencesLogger<TKey> logDifferencesLogger)
{
this.logDifferencesLogger = logDifferencesLogger;
}
}
public interface ILogDifferencesCommand<TModel, TKey>
where TModel : class, IIdModel<TKey>
{
List<LogDifference> CalculateDifferences(TModel x, TModel y);
void LogDifferences(TModel x, TModel y, string tableName, string keyField, string userId, int? clientId);
void RegisterCustomDisplayNameObserver(WeakReference<ICustomDisplayNameObserver<TModel>> observer);
void RegisterCustomChangeDateObserver(WeakReference<ICustomChangeDateObserver<TModel>> observer);
}
public interface ILogDifferencesLogger<TKey>
{
void LogDifferences(string tableName, string keyField, string userId, TKey id, List<LogDifference> differences, int? clientId);
}
The reason TKey is required is because of the IIdModel interface.
One option that comes to mind:
var loggers = new Dictionary<string, ConfiguredInstance>();
loggers.Add("all", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesAllLogger<>)));
loggers.Add("nlog", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesNLogLogger<>)));
loggers.Add("database", c.For(typeof(ILogDifferencesLogger<>))
.Use(typeof(LogDifferencesDatabaseLogger<>)));
foreach (var kv in loggers) {
// if you still need them named
// if you only used names for this concrete scenario - you probably don't
// so can remove it
kv.Value.Named(kv.Key);
}
c.For(typeof(LogDifferencesCommand<>))
.Use(typeof(LogDifferencesCommand<>))
// add explicit instance as dependency
.Dependencies.Add(typeof(ILogDifferencesLogger<>), loggers[AppSettingsManager.Get("logDifferences:Target")]);
Update. As we've figured out in comments, this doesn't work for your specific case when LogDifferencesCommand has multiple type arguments. For some reason (and I think it's a bug) - structure map tries to create closed generic type ILogDifferencesLogger<>, but when doing that - passes generic type arguments from LogDifferencesCommand. Maybe worth firing an issue at their github. You can workaround it like this:
public class GenericTypesWorkaroundInstance : Instance
{
private readonly Instance _target;
private readonly Func<Type[], Type[]> _chooseTypes;
public GenericTypesWorkaroundInstance(Instance target, Func<Type[], Type[]> chooseTypes) {
_target = target;
_chooseTypes = chooseTypes;
ReturnedType = _target.ReturnedType;
}
public override Instance CloseType(Type[] types) {
// close type correctly by ignoring wrong type arguments
return _target.CloseType(_chooseTypes(types));
}
public override IDependencySource ToDependencySource(Type pluginType) {
throw new NotSupportedException();
}
public override string Description => "Correctly close types over open generic instance";
public override Type ReturnedType { get; }
}
And then doing
commandReg.Dependencies.Add(
commandReg.Constructor.GetParameters().First(
p => p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition() == typeof(ILogDifferencesLogger<>)).Name,
new GenericTypesWorkaroundInstance(
loggers[AppSettingsManager.Get("logDifferences:Target")],
// specify which types are correct
types => types.Skip(1).ToArray()));
And it works, but I cannot say that I like it.
The author of StructureMap (me) would strongly advise you try to use conditional registration upfront at application bootstrapping time to select the default logger registrations by checking the configuration value and then just allowing auto-wiring to handle the dependencies at runtime.

How can I get the return type of a Func<T> using reflection?

I have the following type hierarchy:
public abstract class Controller {}
public sealed class PersonController : Controller {}
public sealed class OrderController : Controller {}
I also have a method that resolves the instance of a given type on demand (think of it as a layman's IOC):
private void Resolve<T>(Func<T>[] controllerFactories) where T : Controller
{
Array.ForEach(controllerFactories, x =>
{
// This returns Controller instead of the child class
// I need to know the actual type so that I can do other stuff
// before resolving the instance.
Console.WriteLine(x.Method.ReturnType);
var controllerInstance = x();
});
}
I need to figure out the type of T in Func<T> but when I try:
void Main()
{
var factories = new Func<Controller>[] {
() => new PersonController(),
() => new OrderController()
};
Resolve(factories);
}
I get Controller instead of the PersonController and OrderController.
Any ideas?
Update:
I thank everyone for providing such detailed answers and examples. They prompted me to rethink the API and here is what I finally came up with which serves what I wanted to accomplish:
public interface IResolver<T>
{
void Register<TKey>(Func<TKey> factory) where TKey : T;
T Resolve(Type type);
void ResolveAll();
}
public sealed class ControllerResolver : IResolver<Controller>
{
private Dictionary<Type, Func<Controller>> _factories = new Dictionary<Type, Func<Controller>>();
public void Register<TKey>(Func<TKey> factory) where TKey : Controller
{
_factories.Add(typeof(TKey), factory);
}
public Controller Resolve(Type type) => _factories[type]();
public void ResolveAll()
{
foreach (var pair in _factories)
{
// Correctly outputs what I want
Console.WriteLine(pair.Value.Method.ReturnType);
}
}
}
And here are some usage examples:
void Main()
{
var resolver = new ControllerResolver();
resolver.Register(() => new PersonController());
resolver.Register(() => new OrderController());
resolver.ResolveAll();
resolver.Resolve(typeof(PersonController));
}
Each method has a return type stored in the assembly, you specified that your methods' return type is Controller. This is the only thing that is guaranteed as an information (That is what we know without executing the method - Also at compile time)
So we know that the method should return Controller or anything that derive from that. We can never know what is the run-time type until we actually call that method.
As an example, what if the method has a code like:
var factories = new Func<Controller>[] {
() =>
{
if ( DateTime.Now.Second % 2 == 0 )
return new OrderController();
else
return new PersonController();
}
};
If you need to get the run-time type of the object returned, then you need to:
var controllerInstance = x();
Console.WriteLine(controllerInstance.GetType().Name);
Lambdas in C# get their type based on the expression they are assigned to. For example, this:
Func<string, string> d = x => x;
Makes the lambda x => x a string => string function, on the grounds that this is what the variable d expects.
Why is this relevant? Because in the following code, you're creating a similar expectation for the following lambdas:
var factories = new Func<Controller>[] {
() => new PersonController(),
() => new OrderController()
};
The expectation is that these lambdas are of the Func<Controller> type. If the compiler was looking at the lambda bodies for the type, it would arrive at a different conclusion, specifically that one is a Func<PersonController> and the other is a Func<OrderController>, both of which are kinds of Func<Controller>. But the compiler doesn't look at the body for the type, it looks at the variable and the "variable" in this case is the array slot.
Thus, the compiler will generate these delegates not as these methods:
PersonController Delegate1()
{
return new PersonController();
}
OrderController Delegate2()
{
return new OrderController();
}
But as these methods:
Controller Delegate1()
{
return new PersonController();
}
Controller Delegate2()
{
return new OrderController();
}
Therefore, by the time these delegates are inserted into the array, only the type information of the signatures remain, while the actual type information of the lambda bodies is lost.
But there is one way to maintain the information about the lambda bodies, and then inspect that information instead of the delegate signatures. You can use of expression trees, which are a way to tell the compiler to treat code as data and essentially generate tree objects that represent the lambda, instead of the actual method that implements the lambda.
Their syntax is nearly identical to that of lambdas:
var factories = new Expression<Func<Controller>>[] {
() => new PersonController(),
() => new OrderController()
};
The difference is that now these objects aren't functions, but rather representations of functions. You can traverse those representation and very easily find the type of their top-level expression:
var t0 = factories[0].Body.Type; // this is equal to typeof(PersonController)
var t1 = factories[1].Body.Type; // this is equal to typeof(OrderController)
Finally, you can also turn these representations into actual functions, if you also intend to run them:
Func<Controller>[] implementations = factories.Select(x => x.Compile()).ToArray();
The problem is that the same T has to apply to every type in your array. In
private void Resolve<T>(Func<T>[] controllerFactories) where T : Controller
{
Array.ForEach(controllerFactories, x =>
{
// This returns Controller instead of the child class
Console.WriteLine(x.Method.ReturnType);
var controllerInstance = x();
});
}
what is your T? Is it PersonController? Is it OrderController? To make it fit your use case, it can only be a class that is a superclass of both PersonController and OrderController, while being a subclass of Controller. Therefore T can only be one class: Controller (assuming the two classes don't have a common base class that extends Controller). There is no point in making this class generic. It is exactly the same as this:
private void Resolve(Func<Controller>[] controllerFactories)
{
Array.ForEach(controllerFactories, x =>
{
Console.WriteLine(x.Method.ReturnType);
var controllerInstance = x();
});
}
Instead of an array of Func<Controller> instances, it may make more sense to pass in a dictionary keyed on your return type:
var factories = new Dictionary<Type, Func<Controller>> {
[typeof(PersonController)] = () => new PersonController(),
[typeof(OrderController)] = () => new OrderController()
};
An instance of Func<Controller> simply does not contain enough information to determine the type of its return value on its own: not without evaluating it. And even if you evaluate it, remember that a function is a black box. Just because it returns a PersonController once doesn't mean that it won't return an OrderController the next time you invoke it.
This is how it works, when you are instantiating your array, you are specifying that it would be storing Func objects of type Controller, now you are instantiating the sub types objects inside the array of Func, the Resolve, does not know what is the actual type unless you call GetType() to determine the actual type of it, it just knows that the T will be of type Controller because of the constraint i.e where T : Controller

Infer Generic Type of Interface

-- Context
I have the following 5 objects
IChangeStatus<T>
myFirstClass : IChangeStatus<firstClassEnum>
mySecondClass : IChangeStatus<secondClassEnum>
myClassHandler<TEntity>
myFirstClassHandler : myClassHandler<myFirstClass>
for the purposes of the question we can assume the interface only has the property
T Status { get; }
-- Questions
1.- How can I ask in a method in myClassHandler if an instance of TEntity implements IChangeStatus?
2.- How can I iterate over an IEnumerable of TEntity assuming their specific IChangeStatus?
To check if your class implements IChangeStatus, you can simply do:
public void FooMethod(ClassType myClass)
{
var doesImplementIChange = myClass as IChangeStatus<SomeClass>
if (doesImplementIChange != null)
{
// Do stuff..
}
}
To iterate over an IEnumerable of your classes:
foreach (var data in myClass.OfType<MyType>())
{
// Do stuff..
}
or, you could do:
foreach (var cls in myClass)
{
var myCls = myClass as IChangeStatus<SomeClass>;
if (myCls != null)
{
// Do stuff..
}
}
If you want to use T from IChangeStatus<T> in MyClassHandler, you will have to add another type parameter. For example:
class MyClassHandler<TEntity, TStatus>
where TEntity : IChangeStatus<TStatus>
{
public IEnumerable<TStatus> Statuses
{
get { return _entities.Select(entity => entity.Status); }
}
}
The where clause will ensure that the entity and status types are correlated.
If you don't want to do that, you could add an additional non-generic interface that exposes a Status property of the type Object. You'd lose some static typing that way, but you would not need the extra type parameter.
I found this other SO Question - Check if a type implements a generic interface without considering the generic type arguments which gave me a more generic answer which is what I was looking for:
return entity.GetType().GetInterfaces()
.Where(i => i.IsGenericType)
.Any(i => i.GetGenericTypeDefinition() == typeof(IChangeStatus<>));
As to the iteration over the IEnumerable assuming the specific type of IChangeStatus, since we got that to point then the type does implement the interface thus has a Status property... so I went for dynamic type.

In C# how can i check if T is of type IInterface and cast to that if my object supports that interface?

In C#, I have a function that passes in T using generics and I want to run a check to see if T is an object that implements a interface and if so call one of the methods on that interface.
I don't want to have T constraints to only be of that Type. Is it possible to do this?
For example:
public class MyModel<T> : IModel<T> where T : MyObjectBase
{
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities));
}
return entities;
}
public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
}
The error that I am getting is:
Error 21 The type 'TResult' cannot be used as type parameter 'TResult' in the generic type or method 'FilterMe(System.Collections.Generic.IEnumerable)'. There is no implicit reference conversion from 'TResult' to 'IFilterable'.
The missing piece is Cast<>():
if(typeof(IFilterable).IsAssignableFrom(typeof(T))) {
entities = FilterMe(entities.Cast<IFilterable>()).AsQueryable().Cast<T>();
}
Note the use of Cast<>() to convert the entities list to the correct subtype. This cast would fail unless T implements IFilterable, but since we already checked that, we know that it will.
if (typeof(IMyInterface).IsAssignableFrom(typeof(T))
This checks whether a variable of type IMyInterface can be assigned from an instance of type T.
If you have a generic-type parameter that may or may not implement IFoo, it's possible to as cast it to a storage location of type IFoo; if you do that, you may pass it to any method which expects an IFoo, as well as any method that expects a generic parameter constrained to IFoo, but you will lose all generic type information if you do that--the parameter will be passed as type IFoo. Among other things, this will mean that if your original object was a structure, it will be boxed.
If you wish to test whether a generic parameter type implements IFoo and call a method that takes a generic-constrained IFoo if it does, while keeping the original generic type (this could be useful if the type is a struct, and could be necessary if the type is being passed to a generic method which has both IFoo and IBar constraints and the things one might want to pass do not share any single common supertype), it will be necessary to use Reflection.
For example, suppose one wants to have a method Zap which takes a generic ref parameter, calls Dispose on it if it implements IDisposable, and clears it out. If the parameter is an IDisposable class type, a null test should be performed as an atomic operation with clearing out the parameter.
public static class MaybeDisposer
{
static class ClassDisposer<T> where T : class,IDisposable
{
public static void Zap(ref T it)
{
T old_it = System.Threading.Interlocked.Exchange(ref it, null);
if (old_it != null)
{
Console.WriteLine("Disposing class {0}", typeof(T));
old_it.Dispose();
}
else
Console.WriteLine("Class ref {0} already null", typeof(T));
}
}
static class StructDisposer<T> where T : struct,IDisposable
{
public static void Zap(ref T it)
{
Console.WriteLine("Disposing struct {0}", typeof(T));
it.Dispose();
it = default(T);
}
}
static class nonDisposer<T>
{
public static void Zap(ref T it)
{
Console.WriteLine("Type {0} is not disposable", typeof(T));
it = default(T);
}
}
class findDisposer<T>
{
public static ActByRef<T> Zap = InitZap;
public static void InitZap(ref T it)
{
Type[] types = {typeof(T)};
Type t;
if (!(typeof(IDisposable).IsAssignableFrom(typeof(T))))
t = typeof(MaybeDisposer.nonDisposer<>).MakeGenericType(types);
else if (typeof(T).IsValueType)
t = typeof(MaybeDisposer.StructDisposer<>).MakeGenericType(types);
else
t = typeof(MaybeDisposer.ClassDisposer<>).MakeGenericType(types);
Console.WriteLine("Assigning disposer {0}", t);
Zap = (ActByRef<T>)Delegate.CreateDelegate(typeof(ActByRef<T>), t, "Zap");
Zap(ref it);
}
}
public static void Zap<T>(ref T it)
{
findDisposer<T>.Zap(ref it);
}
}
The first time the code is invoked with any type T, it will determine what sort of generic static class can be produced for that parameter and use Reflection to create a delegate to call a static method of that generic class. Subsequent calls with the same type T will use the cached delegate. Even though Reflection can be a little slow, it will only need to be used once for any type T. All subsequent calls to MaybeDisposer.Zap<T>(ref T it) with the same type T will be dispatched directly through the delegate and will thus execute quickly.
Note that the calls to MakeGenericType will throw exceptions if they are given generic type parameters which do not meet the constraints of the given open generic classes (e.g. if T was a class or did not implement IDisposable, an attempt to make the generic type StructDisposer<T> would throw an exception); such tests occur at run-time and are not validated by the compiler, so you can use run-time checking to see if the types meet appropriate constraints. Note that the code does not test whether it implements IDisposable, but instead tests whether T does. This is very important. Otherwise, if MaybeDispose was called with a parameter of type Object which held a reference to a Stream, it would determine that it implemented IDisposable and thus try to create a ClassDisposer<Object>, crashing because Object does not implement IDisposable.
The simplest form I can come up with is something like:
public IEnumerable<T> GetRecords()
{
IQueryable<T> entities = new List<T>().AsQueryable();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
entities = FilterMe<IFilterable, T>(entities.OfType<IFilterable>()).AsQueryable();
}
return entities;
}
public IEnumerable<TResult> FilterMe<TSource, TResult>(IEnumerable<TSource> linked) where TSource : IFilterable
{
return linked.Where(r => true).OfType<TResult>();
}
The point here is the need to have types to pass into and get back out of the method. I had to change the types locally to get it working.
OfType will silently filter out items that are not really of a given type, so it assumes that it's a collection of the same type in any one call.
Because you are re-assigning from FilterMe, you still need the interface assignable check.
Is the OfType(...) method (link) what you're looking for?
public class MyModel<T> : IModel<T> where T : MyObjectBase
{
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities));
}
return entities;
}
public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.OfType<TResult>().Where(r => dict.ContainsKey(r.Id));
}
}
In my answer, I assume that method FilterMe is used internally and should not be visible outside your model and could be marked private. If my assumption is wrong, you could create a private overload of FilterMe.
In my answer I just removed the generic <TResult>. I assume that this FilterMe always is about then entities of type T (since it is in the same Model class). This solves the problem about casting between T and TResult. TResult does not have to be marked as IFilterable since none of the members of IFilterable are used. And since the code already checks if T is IFilterable why check again (especially when FilterMe would be private)?
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities).AsQueryable();
}
return entities;
}
public IEnumerable<T> FilterMe(IEnumerable<T> linked)
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
If you would create a second FilterMe, replace the IEumerable<T> types with Queryable<T>, so you do not have to convert your entities with AsQueryable().
public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
Try replacing FilterMe with this version:
public IEnumerable<T> FilterMe(IEnumerable<IFilterable> linked)
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id)).Cast<T>();
}
Then, were you call, change your code to this:
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
var filterable = entities.Cast<IFilterable>();
entities = FilterMe(entities).AsQueryable();
}
You don't have to make the FilterMe method a generic method to achieve the same result.
public class MyModel<T> : IModel<T> where T : MyObjectBase {
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities.Cast<IFilterable>());
}
return entities;
}
public IEnumerable<T> FilterMe(IEnumerable<IFilterable> linked) {
var dict = GetDict();
return linked
.Where(r => dict.ContainsKey(r.Id))
.Cast<T>();
}
}

Categories