Приведите PropertyInfo к универсальному типу - PullRequest
4 голосов
/ 10 июня 2011

У меня есть следующий класс:

public class AuthContext : DbContext
{
    public DbSet<Models.Permission> Permissions { get; set; }
    public DbSet<Models.Application> Applications { get; set; }
    public DbSet<Models.Employee> Employees { get; set; } 
    // ...
}

Я создал метод расширения Clear() для типа DbSet<T>.Используя отражение, я могу проверить экземпляр AuthContext и прочитать все его свойства типа DbSet<T> как PropertyInfo[].Как я могу привести PropertyInfo к DbSet<T>, чтобы вызвать для него метод расширения?

var currentContext = new AuthContext();
...
var dbSets = typeof(AuthContext).GetProperties(BindingFlags.Public | BindingFlags.Instance);
dbSets.Where(pi =>
                pi.PropertyType.IsGenericTypeDefinition &&
                pi.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)).ToList()
      .ForEach(pi = ((DbSet<T>)pi.GetValue(currentContext, null)).Clear()); // !!!THIS WILL NOT WORK

Ответы [ 4 ]

4 голосов
/ 10 июня 2011

Пожалуйста, смотрите ответ Андраса Золтана для объяснения того, что вы делаете неправильно.

Однако, если вы используете .NET 4.0, вам не нужно использовать отражение для вызова метода, вы можете просто использовать новое ключевое слово dynamic:

var currentContext = new AuthContext();
var dbSets = typeof(AuthContext).GetProperties(BindingFlags.Public | 
                                               BindingFlags.Instance);
dbSets.Where(pi => pi.PropertyType.IsGenericType &&
                   pi.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
      .ToList()
      .ForEach(pi => ExtensionClass.Clear((dynamic)pi.GetValue(currentContext, 
                                                               null)));

Я изменилприведение от DbSet<T> к dynamic и изменение способа вызова метода.
Поскольку Clear является методом расширения, его нельзя вызвать непосредственно для типа dynamic, поскольку dynamic не делаетне знаю о методах расширения.Но так как методы расширения не намного больше, чем статические методы, вы всегда можете изменить вызов метода расширения на обычный вызов статического метода.
Все, что вам нужно сделать, это заменить ExtensionClass на реальное имя классав котором определяется Clear.

1 голос
/ 10 июня 2011

Ваше приведение неверно.

Вы не можете привести к (DbSet<T>), потому что это не конкретный тип, если T не определен внутри универсального метода или универсального типа.

ВыУ меня есть пара возможностей.

Если у DbSet есть базовый класс (например, DbSet_BaseClass в моем коде ниже), из которого вы все еще можете реализовать свой метод Clear() - измените его сигнатуру с:

public static void Clear<T>(this DbSet<T>)

на:

public static void Clear(this DbSet_BaseClass)

Затем вы можете изменить свой актерский состав в .ForEach на ((DbSet_BaseClass)pi.GetValue...

Если вы не можете этого сделать, вы можете отразить-вызватьметод расширения Clear путем создания его конкретной универсальной версии для T DbSet<T>:

MethodInfo myClearMethod = typeof(container_type).GetMethod(
  "Clear", BindingFlags.Public | BindingFlags.Static);

Затем, учитывая информацию о свойстве и экземпляр контекста:

Type propType = pi.PropertyType;
Type typeofT = propType.GetGenericArguments[0];
MethodInfo toInvoke = myClearMethod.MakeGenericMethod(typeofT);
//now invoke it
toInvoke.Invoke(null, new[] { pi.GetValue(currentContext, null) });

Существует множество оптимизаций, которые можно поставить поверх этого, кэширование делегатов и т. Д., Но это будет работать.

Обновление

Или см. Ответ @Daniel Hilgarth для классный способ динамической отправки вызова к методу расширения без необходимости делать какие-либо извыше (динамическая диспетчеризация делает что-то подобное, но для вас все кэшируется сверху).Если бы это был я - я бы использовал это.

0 голосов
/ 10 июня 2011

Вы должны сделать отражение в DbSet, чтобы вызвать метод очистки

Попробуйте это:

var dbSets = typeof(AuthContext).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            dbSets.Where(pi =>
                            pi.PropertyType.IsGenericType &&
                            pi.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)).ToList()
                  .ForEach(pi =>
                      {
                          typeof(DbSet<>)
                              .MakeGenericType(pi.PropertyType.GetGenericArguments()[0])
                              .GetMethod("Clear")
                              .Invoke(pi.GetValue(currentContext, null), null);
                      }
                      );
0 голосов
/ 10 июня 2011

Вы не можете разыгрывать типы, потому что они не имеют никакого отношения друг к другу.Вы получаете PropertyInfo, который сообщает вам о типе, но не сам тип.

Я думаю, вы захотите использовать Type.GetMethod, чтобы найти метод «Clear», как MethodInfoи тогда вы сможете вызывать MethodInfo.Invoke.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...