RuntimeBinderException с динамическим в C # 4.0 - PullRequest
6 голосов
/ 15 апреля 2010

У меня есть интерфейс:

public abstract class Authorizer<T> where T : RequiresAuthorization
{
    public AuthorizationStatus Authorize(T record)
    {
        // Perform authorization specific stuff
        // and then hand off to an abstract method to handle T-specific stuff
        // that should happen when authorization is successful

    }
}

Затем у меня есть куча разных классов, каждый из которых реализует RequAuthorization и, соответственно, Authorizer<T> для каждого из них (для каждого бизнес-объекта в моем домене требуется разная логика для выполнения после авторизации записи).

Я также использую UnityContainer, в котором я регистрирую различные Authorizer<T>. Затем у меня есть следующий код, чтобы найти нужную запись в базе данных и авторизовать ее:

void Authorize(RequiresAuthorization item)
{
    var dbItem = ChildContainer.Resolve<IAuthorizationRepository>()
                               .RetrieveRequiresAuthorizationById(item.Id);
    var authorizerType = type.GetType(String.Format("Foo.Authorizer`1[[{0}]], Foo",
                             dbItem.GetType().AssemblyQualifiedName));
    dynamic authorizer = ChildContainer.Resolve(type) as dynamic;

    authorizer.Authorize(dbItem);
}

Обычно я использую идентификатор объекта для извлечения его из базы данных. В фоновом режиме NHibernate позаботится о том, чтобы выяснить, какой тип требует разрешения . Затем я хочу найти правильный Authorizer для него (я не знаю во время компиляции, какая реализация Authorizer<T> мне нужна, поэтому я немного подумал, чтобы получить полностью определенный тип). Для этого я использую неуниверсальную перегрузку метода Resolve UnityContainer, чтобы найти правильный конфигуратор из конфигурации.

Наконец, я хочу вызвать Authorize на авторизаторе, проходя через объект, который я получил от NHibernate.

Теперь по проблеме:

В Beta2 VS2010 приведенный выше код работает отлично. На RC и RTM, как только я выполняю вызов Authorize (), я получаю исключение RuntimeBinderException, говорящее: «Наилучшее совпадение перегруженного метода для 'Foo.Authorizer<Bar>.Authorize(Bar)' имеет недопустимые аргументы». Когда я проверяю авторизатор в отладчике, это правильный тип. Когда я вызываю GetType (). GetMethods (), я вижу метод Authorize, который принимает Bar. Если я использую GetType () для dbItem, это Bar.

Поскольку это работало в Beta2, а не в RC, я предположил, что это регрессия (кажется, что это должно работать), и я отложил сортировку до тех пор, пока у меня не будет возможности протестировать его на RTM-версии C # 4.0. Теперь я сделал это, и проблема все еще сохраняется. У кого-нибудь есть предложения сделать эту работу?

Спасибо

Terence

1 Ответ

11 голосов
/ 20 апреля 2010

Теренс, мне нужно больше информации о типах в игре и их определениях, чтобы понять, в чем на самом деле проблема, но эта ошибка в основном говорит вам, что она не может преобразовать dbItem в Bar. Есть две возможности:

1) RetrieveRequiresAuthorizationById() возвращает dynamic, и поэтому тип времени компиляции dbItem выводится как dynamic. Если это так, то средство связывания во время выполнения выберет тип для dbItem во время выполнения, который по сути является наилучшим доступным типом, если можно найти. Этот тип не конвертируется в Bar с учетом доступности. Например, может случиться так, что тип времени выполнения dbItem - это некоторый недоступный тип с прямым базовым классом object, и, очевидно, object не конвертируется в Bar.

2) RetrieveRequiresAuthorizationById() возвращает некоторый статический тип. В этом случае статический тип не может быть преобразован в Bar во время выполнения.

Я предполагаю, что (2) имеет место, и тип dbItem равен RequiresAuthorization. Который я также предполагаю, является интерфейсом. И это не будет конвертируемо в любой тип класса.

Если я прав, то вы хотите сделать dbItem динамическим. Если вы это сделаете, то связыватель времени выполнения выберет соответствующий тип для dbItem, что, вероятно, является единственной причиной, по которой вы хотите это сделать.

void Authorize(RequiresAuthorization item)
{
    var dbItem = ChildContainer.Resolve<IAuthorizationRepository>()
                               .RetrieveRequiresAuthorizationById(item.Id);
    var authorizerType = type.GetType(String.Format("Foo.Authorizer`1[[{0}]], Foo",
                             dbItem.GetType().AssemblyQualifiedName));
    dynamic authorizer = ChildContainer.Resolve(type) as dynamic;

    authorizer.Authorize(dbItem as dynamic); // <<<Note "as" here
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...