Как я могу использовать отражение для вызова метода DomainService.Load? - PullRequest
0 голосов
/ 25 мая 2011

Я пытаюсь создать служебный метод, который будет загружать коллекции объектов с помощью Reflection.Идея состоит в том, что программист, использующий утилиту, может указать любой тип сущности, и этот метод обнаружит правильный EntityQuery и загрузит контекст с тем, что они запросили.Итак, я получил от пользователя тип Entity и предложение Where, теперь я пытаюсь выяснить, как вызвать метод.Вот что у меня есть:

public void Handle(LoadEntityQuery loadQuery, Action<LoadEntityQueryResult> reply)
{
    foreach (var entry in loadQuery.Entities)
    {

        Type entityType = entry.Key;
        Type _contextType = EmployeeJobsContext.Instance.GetType();

        MethodInfo _methodInfo = (from x in _contextType.GetMethods()
                                 where x.ReturnType.BaseType == typeof(EntityQuery)
                                 from y in x.ReturnType.GetGenericArguments()
                                 where y == entityType
                                 select x).FirstOrDefault();
        if (_methodInfo != null)
        {
            var query = _methodInfo.Invoke(EmployeeJobsContext.Instance, null);

           var _loadMethods = from x in _contextType.GetMethods()
                              where x.Name == "Load" &&
                                    x.GetParameters().Length == 3
                              select x;
           MethodInfo _loadMethod = null;

           if (_loadMethods != null)
           {
               foreach (MethodInfo item in _loadMethods)
               {
                   ParameterInfo[] _paramInfo = item.GetParameters();
                   if (_paramInfo[0].ParameterType.BaseType == typeof(EntityQuery) &&
                       _paramInfo[1].ParameterType.IsGenericType &&
                       _paramInfo[1].ParameterType.GetGenericArguments().Length == 1 &&
                       _paramInfo[1].ParameterType.GetGenericArguments()[0].BaseType == typeof(LoadOperation) &&
                       _paramInfo[2].ParameterType == typeof(object))
                   {
                       _loadMethod = item;
                       break;
                   }
               }
           }

           MethodInfo _loadOpMethod = this.GetType().GetMethod("LoadOperationResult");
           Delegate d = Delegate.CreateDelegate(typeof(LoadOpDel), _loadOpMethod);

           if (_loadMethod != null)
           {
               object [] _params = new object[3];
               _params[0] = query;
               _params[1] = d;
               _params[2] = null;

               _loadMethod = _loadMethod.MakeGenericMethod(entityType);
               _loadMethod.Invoke(_context, _params);
           }
        }           
    }
}

public delegate void LoadOpDel(LoadOperation loadOp);

public void LoadOperationResult (LoadOperation loadOp)
{
    if (loadOp.HasError == true)
    {
        //reply(new LoadEntityQueryResult { Error = loadOp.Error.Message });
        loadOp.MarkErrorAsHandled();
    }
} 

Цикл foreach выполняет итерацию словаря >>, где Key - это тип Entity, а значением - предложение Where.Первая часть кода находит правильный метод EntityQuery и вызывает его для получения фактического запроса.Затем он обнаруживает правильную перегрузку Load (я знаю, вероятно, есть лучший способ найти метод :)). Эта часть кода работает правильно, я могу обнаружить правильный EntityQuery и метод Load.

Для LoadOperation я хочу использовать LoadOperationResult в качестве метода моего делегата.Однако когда я пытаюсь запустить этот код, я получаю исключение о том, что тип делегата и сигнатуры типа метода не совпадают.Я почти уверен, что моя подпись верна, потому что, если бы я должен был вызывать Load напрямую и передавать имя функции как обратный вызов, этот код выполнялся бы правильно.Я довольно хорошо знаком с рефлексивным программированием, однако добавление в микс обратных вызовов Generics и Action немного выше моего уровня на данный момент.Я в растерянности относительно того, что я делаю неправильно, у кого-нибудь есть какие-нибудь указатели для меня?Я далеко?Спасибо за вашу помощь!!Jason

Ответы [ 3 ]

0 голосов
/ 25 мая 2011

, даже если Action<LoadOperation> и LoadOpDel имеют одинаковую подпись, которую вы не можете неявно конвертировать между ними.В C # принуждении типа иногда кажется, что это не так, но если вы используете отражение типа, очевидно, что оно не сработает.

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

Я обнаружил, что мне не нужно использовать отражение для вызова метода Load (отрицая необходимость делегата), вместо этого я вызываю Load напрямую, создавая универсальный метод на основе типа Entity.Вот то, что я придумал для всех, кто заинтересован:

    /// <summary>
    /// The Action callback for the LoadEntityQuery handler. This callback is used to respond to the 
    /// LoadEntityQuery when all Load calls are complete. See the Handle method 
    /// </summary>
    private Action<LoadEntityQueryResult> _reply = null;

    /// <summary>
    /// Accumulator used to determine when the last entity has been loaded
    /// </summary>
    private int EntityCount { get; set; }

    /// <summary>
    /// Collective error container for Errors from the LoadOperation. This is value is returned via
    /// the _reply callback to the calling code.
    /// </summary>
    private List<Exception> Errors = null;

    public void Handle(LoadEntityQuery loadQuery, Action<LoadEntityQueryResult> reply)
    {
        _reply = reply;
        Errors = new List<Exception>();
        EntityCount = loadQuery.Entities.Count();

        MethodInfo _loadOpMethod = this.GetType().GetMethod("Load", BindingFlags.NonPublic | BindingFlags.Instance);
        int _entityCount = loadQuery.Entities.Count();

        foreach (var entry in loadQuery.Entities)
        {
            Type entityType = entry.Key;
            Type _contextType = EmployeeJobsContext.Instance.GetType();

            MethodInfo _methodInfo = (from x in _contextType.GetMethods()
                                      where x.ReturnType.BaseType == typeof(EntityQuery)
                                      from y in x.ReturnType.GetGenericArguments()
                                      where y == entityType
                                      select x).FirstOrDefault();
            if (_methodInfo != null)
            {
                var query = _methodInfo.Invoke(EmployeeJobsContext.Instance, null);
                MethodInfo _typedLoadOpMethod = _loadOpMethod.MakeGenericMethod(new Type[] { entityType });

                _typedLoadOpMethod.Invoke(this, new[] { query, entry.Value});
            }
        }
    }

    private void Load<T>(EntityQuery<T> query, Expression<Func<T, bool>> where) where T: Entity
    {
        if (where != null)
            query = query.Where(where);

        EmployeeJobsContext.Instance.Load(query, (loadOp) =>
            {
                EntityCount--;
                if (loadOp.HasError)
                {
                    Errors.Add(loadOp.Error);
                    loadOp.MarkErrorAsHandled();
                }

                if (EntityCount == 0)
                    _reply(new LoadEntityQueryResult { ErrorList = Errors });

            }, null);
    }

Обработчик операции Load следит за завершением загрузки последней сущностью, а затем отвечает клиенту, что загрузка завершена (с любыми ошибками, еслиони происходят).

0 голосов
/ 25 мая 2011

Ну, ничего не зная о классах, которые вы используете, я не могу по-настоящему протестировать свое решение, но когда я выну создание делегата из цикла for, я могу заставить его работать.Я изменил целевой метод на статический:

public static void LoadOperationResult(LoadOperation loadOp)

, и делегат был создан без проблем.

Я, скажем, не особенно компетентен в этой области, но я думаю, что вы хотите создать делегата один раз и просто использовать его повторно, когда вам это нужно.Зачем создавать его снова и снова?

...