Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Invalid type owner for DynamicMethod" error when sorting an Interface

We're using Andrew Davey's BindingListView<T> class via sourceforge to bind collections to a DataGridView and allow sorting and filtering.

This works fine for normal collections. In one case however the collection we're binding to is an Interface type and we get this error if we try to sort on it:

Invalid type owner for DynamicMethod

The error is deep in Andrew Davies' code so it's hard for us to know where to start.

        private static Comparison<T> BuildValueTypeComparison(PropertyInfo pi, ListSortDirection direction)
        {
            MethodInfo getMethod = pi.GetGetMethod();
            Debug.Assert(getMethod != null);


            DynamicMethod dm = new DynamicMethod("Get" + pi.Name, typeof(int), new Type[] { typeof(T), typeof(T) }, typeof(T), true);
            //^^^ ======== Here's the line reporting the error=========== ^^^

            ILGenerator il = dm.GetILGenerator();

            // Get the value of the first object's property.
            il.Emit(OpCodes.Ldarg_0);
            il.EmitCall(OpCodes.Call, getMethod, null);
            // Box the value type
            il.Emit(OpCodes.Box, pi.PropertyType);

            // Get the value of the second object's property.
            il.Emit(OpCodes.Ldarg_1);
            il.EmitCall(OpCodes.Call, getMethod, null);
            // Box the value type
            il.Emit(OpCodes.Box, pi.PropertyType);

            // Cast the first value to IComparable and call CompareTo,
            // passing the second value as the argument.
            il.Emit(OpCodes.Castclass, typeof(IComparable));
            il.EmitCall(OpCodes.Call, typeof(IComparable).GetMethod("CompareTo"), null);

            // If descending then multiply comparison result by -1
            // to reverse the ordering.
            if (direction == ListSortDirection.Descending)
            {
                il.Emit(OpCodes.Ldc_I4_M1);
                il.Emit(OpCodes.Mul);
            }

            // Return the result of the comparison.
            il.Emit(OpCodes.Ret);

            // Create the delegate pointing at the dynamic method.
            return (Comparison<T>)dm.CreateDelegate(typeof(Comparison<T>));
        }
like image 925
hawbsl Avatar asked May 22 '12 09:05

hawbsl


1 Answers

Do not give up and go to use DataSet! You are heading the right direction! Now, let's first take a look at the function signature:

DynamicMethod(string name, 
              Type returnType, 
              Type[] parameterTypes, 
              Type owner, 
              bool skipVisibility)

Error is “Invalid type owner for DynamicMethod"

The error message is trying to tell you, Type owner is not what the function is expecting. It wants a Class type. You are probably passing an Interface type into the Type owner. It is impossible to create dynamic methods on an Interface.

1.Error Example

Maybe you are using Dependency Injection, and you may like to use Interface.

However, this code will hit Runtime Error.

var viewModelList = GetViewModels(); //returns an IList<IViewModel> <-- has i !!
var blv = new BindingListView<IViewModel>(viewModelList);

2.Working Example

Try to redesign your code to use Concrete Type.

Now, this code will NOT hit Runtime Error

var viewModelList = GetViewModels(); //returns an IList<ViewModel> <-- has no i !!
var blv = new BindingListView<ViewModel>(viewModelList);

Then, your Sorting and Filtering on DataGridView will work automagically :)

EDIT ---------------------------------------------

P.S. About trying to redesign your code:

If you are using a MVVM/MVP pattern, think about the following logic. The IList<IViewModel> should stay on the "VM+P" side. Purpose of using IViewModel is mainly because I want to be able to replace it with, say, MockingViewModel : IViewModel for unit testing the "VM+P" side of logics.

Now, the BindingListView<ViewModel> should really be on the "V" side, that is, the YourView : System.Windows.Form { ... }. And it will be binded to the binding source from there YourBindingSource.DataSource = blv; Since I won't be unit testing a WinForm, any logic in it I will try to refactor them into presenters and viewmodels and keep the View as thin as possible. So, I would just use ViewModel in BindingListView, not the interface IViewModel.

So the BindingListView<ConcreteViewModel> will naturally make sense to me that it doesn't accept a model Interface.

Refer to this question about MVVM MVP design and unit testing WinForm: Should I unit-test my view in MVP(or VM) or how to keep the code in the view to a minimum?

like image 156
Tom Avatar answered Sep 20 '22 13:09

Tom