4

Is it possible to write an assembly which dynamically generates/emits a new class and patches itself to include the new class?

How?

starblue
  • 55,348
  • 14
  • 97
  • 151
Triynko
  • 18,766
  • 21
  • 107
  • 173
  • What exactly do you want to achieve? There could be another way for what you want to do, I guess. – Kirtan May 20 '09 at 04:35
  • Just trying to clear the question. In C# it is possible to use System.CodeDom and System.CodeDom.Compiler to generate and compile code at runtime, load the resulting new assembly and call it via reflection. Your question sounds like this is not what you want. You want to patch an existing assembly. Do I understand that correctly? – Accipitridae May 20 '09 at 05:28
  • I have an assembly deploying to SQLServer, which has data classes tagged with custom attributes. For each tagged class, I create a static method, to be registered as a CLR scalar-valued function in SQLServer, which will be used in check constraints. Each function attempts to instantiate one class, and returns true for success. This unifies the data types in my application with the database. To avoid maintaining that list of functions, I am dynamically generating the class and the functions. This class used to be hand-coded in that same assembly, but now its dynamic. I want a single dll. – Triynko May 20 '09 at 15:56
  • These simple "data" classes represent constrained strings and numbers. Construction fails when the string or number violates a constraint, which is a regex/min_length/max_length for strings or a boolean lambda expression (range check or formula) for numbers. Generated methods therefore function as value constraints, implemented by a test-construction of a data class instance. Having two separate assemblies is problematic: e.g. dependency errors when updating, or "An error occurred while gathering metadata from assembly 'assemblyname' with HRESULT 0x80131130." trying to create clr-function. – Triynko May 20 '09 at 16:05
  • When I redeploy the original assembly (alter assembly) in SQLServer, it succeeds. But when I update the dynamic counterpart, which references it, the alteration fails, although I can delete/recreate it. The error is "ALTER ASSEMBLY failed because the identity of a referenced assembly 'database' has changed. Make sure the version, name, and public key have not changed. ALTER ASSEMBLY failed because the referenced assemblies would change. The referenced assembly list must remain the same." The dynamic assembly looks fine in Reflector, but I must be doing something wrong or there's a bug. – Triynko May 20 '09 at 16:13
  • In other words... I used to have a class in an assembly. The class was removed and replaced with a function that generates the class dynamically instead. I'd like the dynamically generated class to be included the assembly that creates it, so I can deploy a single dll to SQL server. ILMerge seems to work, but I thought there may be a way that doesn't involve creating an intermediate assembly, followed by calling a command-line utility. – Triynko May 20 '09 at 16:26

2 Answers2

2

I asked this question another way here: Using AssemblyBuilder, how can I make all or any of the referenced assemblies embedded instead of linked in the saved assembly?

Patching the existing dll with the dynamically generated code would result in the same thing as embedding the original dll in the dynamically generated code -- a single assembly with the contents of both.

It seems that one way or another, to eliminate dependencies and pack the contents of multiple assemblies into one, the ILMerge utility is the most elegant solution.

The only problem with it is that the types generated in the merged dll are incompatible with the same types in both of the original dlls. If the original DLL, for example, emits a new assembly, merges it with itself, and loads the new assembly... it cannot use its own types to refer to things in the new assembly which correspond to the same type in either of the original assemblies.

In other words: Class A in [dll_generator] references [dll_1]. Class A generates [dll_2], which is based on and of course also references [dll_1]. Class A calls ILMerge to combine [dll_2] with its dependency [dll_1] to produce [dll_merged]. None of the types in [dll_merged] are compatible with any of their original types in [dll_1] and [dll_2], so if class A loads [dll_merged] and tries to do anything with it involving literal type names from its original reference to [dll_1], it fails, because the types are incompatible. The only way class A can work with types in [dll_merged] is to load them by name and work entirely with 'Type' objects and reflection -- or dynamically compile source code against the new [dll_merged].

Community
  • 1
  • 1
Triynko
  • 18,766
  • 21
  • 107
  • 173
1

The best way to do this is to use dependency injection / inversion of control or even a simple service locater.

Your new assembly would create a new concrete implementation and register that instead of the old implementation.

I'm certain that anything more exotic would be a terrible hack indeed.

Luke Schafer
  • 9,209
  • 2
  • 28
  • 29
  • Could you explain this more? Basically, I have an assembly to deploy, and the class that used to be hand-coded in it is now generated dynamically. I want this class in the original assembly, but I wanted it dynamically generated by the assembly, rather than hand-coded by me. Is that possible? – Triynko May 20 '09 at 16:07
  • something like autofac or windsor can be used to register concrete implementations of classes that are then injected from or retreived from the container. It's a simple way to organise your implementations (and that's a terribly simple explaination). If you re-architect your software such that the original implementation is drawn from a container or service locator, then you can easily override that with a custom implementation (as long as the original isn't sealed) – Luke Schafer May 20 '09 at 22:39
  • I don't think that's what I'm looking for. The data classes already exist, and I was just extending the dll to have auto-generated functions which instantiate each data class, and can be registered as CLR functions in SQL Server. This requires the dynamically generated code to be included in the original dll. So far, the solution I'm using is to just use ILMerge as a post-emit step, and that works fine. – Triynko May 21 '09 at 13:45