1

We've developed a DSL to help in coding Orchard custom modules. In the generated driver's Editor method, we're using partial methods to allow for the programmer to override the genrated code behavior, if needed.

At runtime, though, we're getting an exception that the partial method isn't implemented.

A first chance exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in Microsoft.CSharp.dll

Additional information: 'MyModule.Drivers.CompanyPartDriver' does not contain a definition for 'CustomEditorGet'

According to the C# spec, it doesn't have to be, so I'm wondering if the dynamic compilation (or somesuch) is getting in the way here. Note that the code is manually compiled prior to run and debug, so there shouldn't be any code that needs compiled at runtime.

The bit in question is as follows:

public partial class CompanyPartDriver : ContentPartDriver<CompanyPart>
{
   // other code

   partial void CustomEditorGet(CompanyPart part, dynamic shapeHelper, ref DriverResult result);

   protected override DriverResult Editor(CompanyPart part, dynamic shapeHelper)
   {
      DriverResult result;

      if (AdminFilter.IsApplied(HttpContext.Current.Request.RequestContext))
         result = ContentShape("Parts_CompanyAdmin_Edit",
                               () => shapeHelper.EditorTemplate(TemplateName: "Parts/CompanyAdmin",
                                                                Model: part,
                                                                Prefix: Prefix));
      else
         result = ContentShape("Parts_Company_Edit",
                               () => shapeHelper.EditorTemplate(TemplateName: "Parts/Company",
                                                                Model: part,
                                                                Prefix: Prefix));


      CustomEditorGet(part, shapeHelper, ref result);

      return result;
   }

   // other code
}

Adding the "CustomEditorGet" method implementation in a partial class in another file, even if empty, all is fine. Just adding the partial class without the partial method impl doesn't fix it.

Any thoughts?

Michael Sawczyn
  • 305
  • 3
  • 10
  • You do not give the full stack trace of the exception. Are you sure you show everything relevant here? Be aware that when no "part" of the `partial class` actually provides an implementation of the `partial void` method, that method is considered non-existent _at compile-time_. So it is not actually translated into IL, and every call to it is removed. However, it does not explain the problem you see, so I think I need more info. Can you reproduce with a _complete_ and self-contained code sample? – Jeppe Stig Nielsen Aug 20 '14 at 23:06
  • Ah, sorry, I did not see that you used an argument of type `dynamic` in your invocation. In that case the "binding" is post-poned until run-time. But the `partial void` method will not actually exist at run-time. – Jeppe Stig Nielsen Aug 20 '14 at 23:15

2 Answers2

1

(I hope someone cares to provide a more precise answer.)

It looks like you have hit a "bad" cocktail of partial methods and dynamic arguments.

With the partial void method where no "part" of the partial class supplies an implementation, the partial void method is considered "non-existent" at compile-time, and it is not actually realized in the IL.

With the invocation CustomEditorGet(part, shapeHelper, ref result); where the second argument is an expression of compile-time type dynamic, we "bind" to a method that does not exist. Normally, when no dynamic is involved, this entire call/invocation is "removed" at compile-time. But because this is a dynamic expression, we have to post-pone the "binding" until run-time. "Maybe shapeHelper will prove to have a very 'lucky' type that actually finds a method CustomEditorGet to invoke?" So you get the exception at run-time.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • Rats. So no using partial methods in this situation to allow programmers to inject custom functionality into generated code. I may have to switch to static events set in the static constructor, although that feels less elegant than partial methods. Thanks for helping clarify. (Oh, and I'd vote up if I had enough points to do so ... not there yet!) – Michael Sawczyn Aug 21 '14 at 02:38
0

Looks to me like a misuse of partial classes where the right construct is an abstract method. Much more DRY.

Bertrand Le Roy
  • 17,731
  • 2
  • 27
  • 33
  • Could you please clarify? Are you suggesting the generated code should be a base class for the final part class, where the abstract method would then be implemented? Or is it that we should generate the part and require the programmer to derive from that and implement the abstract methods? – Michael Sawczyn Aug 21 '14 at 13:29
  • In either case, the programmer would have to do more work than I want them to have to do for the mainstream use case where no changes are required, since the abstract method would have to be implemented although the implementation would be empty as the programmer doesn't need anything overridden. Also, could you clarify where you see the repetition? Certainly the generated code is repeated, but that's generated code -- I don't expect it to be DRY. Thanks. – Michael Sawczyn Aug 21 '14 at 13:29
  • No: generating a base class would be silly. The first question to ask is why you're generating code. Generated code still needs to be managed, and comes with all sorts of disadvantages. Partial piles on its own problems. Not to mention that it is never used anywhere in Orchard. I think you should have a base class, but generic instead of generated. Derived classes can implement the variations. If some of these implementations need to be empty, just have an empty implementation in the base class and make it virtual instead of abstract. – Bertrand Le Roy Aug 21 '14 at 22:04