none
Adding compilation symbols dynamically to an assembly loaded at runtime RRS feed

  • Question

  • Is it possible to load an assembly using Assembly.Load or a similar method and pass in compilation symbols?

    Some background for this question:

    I have an assembly that defines a type, that I wish to only apply an attribute to when the assembly is loaded at runtime in a given context.

    So my project A defines a type, and based on a pre-processor directive #if statement which checks for the existence of a compilation symbol, an attribute is added to this type. To apply this compilation symbol at runtime I've had to create project B that has this compilation symbol set, and references project A.

    This causes issues later on when I have a reference to the type from project B that I then have to manually map to it's identical type in project A.

    Is there a way I can just have project A and just apply this compilation target at runtime?

    Friday, November 1, 2019 10:55 AM

All replies

  • Just as an update to this I've also tried to just reference project A directly, creating a ProjectReference against my referencing project, then specified the symbols using the following inside this reference:

    <Properties>DefineConstants=XXX</Properties>

    I was convinced this would work, but unfortunately when calling Assembly.Load in the referencing project, the attribute doesn't seem to be coming through.

    Friday, November 1, 2019 11:40 AM
  • No. Listen to what you just said "I want to load an assembly...pass in compilation symbols". Load is a runtime feature. Compilation symbols are compile time. You cannot adjust the compilation symbols of something and it have any effect without recompiling the code. Since you're loading an assembly you don't have the code (most likely) so you cannot recompile it. If you did have the code then you wouldn't load the assembly, you'd use the CodeDom (or Roslyn) to compile the code using the appropriate symbols and it would give you the assembly.

    Given your example, in project A the attribute(s) are applied to the type at the point of compilation. If you compile your code without your preprocessor directive you'll see there is no attribute if you decompile the code. Hence you cannot dynamically add/turn it on at runtime because it doesn't exist in the assembly.

    I think you're solving the underlying issue the wrong way. Attributes are for providing metadata that doesn't change. If you need to make decisions at runtime based upon context then attributes aren't the correct solution. There are any # of other possibilities including a member that tells you whether something is supported or not (e.g. stream read/write), using an interface with different implementations defined and using IoC or some other runtime configuration to determine which to use (e.g. swapping database providers) or simply checking in code and throwing an exception if something is not supported (e.g. Seek() when you're already add the end of the stream).

    Can you provide more specific details about why you need an attribute, what the attribute tells your code, the context in which you wish to use it and what happens if it doesn't?


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, November 1, 2019 1:59 PM
    Moderator
  • Thanks for the reply CoolDad. The attribute that I'm applying is for a package called NJsonSchema to dictate how it generates a JSON schema a given type.

    The type lives inside a class library that's referenced from one project (an ASP.NET web app), which doesn't have a reference to NJsonSchema, and in this context it's used for just evaluation / validation. An added complication is that this type is also persisted to & loaded from a database in this context.

    The class library is also referenced from another project (console app) that just generates the JSON schema, and this attribute should be applied.

    I'm trying not to duplicate this type for both contexts, as this could become a maintenance headache as the types library grows.

    If the attribute isn't applied at all then NJsonSchema fails to represent class inheritance in the schema that it generates.

    Friday, November 1, 2019 2:38 PM
  • You aren't going to be able to use conditional compilation for this. Composition or a surrogate would be the better approach. In composition you should wrap the type in a higher level type that contains the schema information. Your console app would use this type. The class library type would not be attributed. If it is a simple attribute then inheritance would be sufficient.

    public class MyData
    {
       public int Id { get; set; }
       public string Name { get; set; }
    }
    
    //Used only for serialization
    [NJsonSchema]
    public class MySerializableData : MyData
    {
    }

    Ideally you might be able to do this with a generic type instead if it is really just adding an attribute to the type.

    [NJsonSerialable]
    public class JsonSerializable<T>
    {
       public T Data { get; set; }
    }
    
    var data = new JsonSerializable<MyData>();
    
    If you need to set stuff on each property then a surrogate would work better. Most serialization systems allow you to specify a separate type that is responsible for serialization, the whole single responsibility principal thing.

    If you're referring to this package then I don't see that it requires any specific JSON-stuff besides the standard .NET data annotations. I see it supports surrogates vis a `JsonSchemaProcessor` type. It also seems to rely on JSON.NET stuff. The Settings type used for the generator seems to support specifying processors. It looks like they are for global processors but I assume you could get it to work on a type based by filtering the data. However you should probably post this question in that libraries Github repo.


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, November 1, 2019 2:58 PM
    Moderator
  • Thanks Michael, I think adding a global processor to the schema generator settings sounds like it'd be the most convenient in this instance.

    I'll have a play around with it and see how far I get.

    Friday, November 1, 2019 4:48 PM