Click here to Skip to main content
15,886,110 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hey,

I'm writing a piece of code that deserializes incoming json messages into classes. The scenario is pretty common and well-known. I'm implementing a Command design pattern, which learns the target class type, according to a matched identifier. I'm willing to have my system support plugins (that would be capable of implementing protocol abstract commands, and even create tables in my database - the best example I can think of is having several authentication methods).

Every class that inherits from a certain polymorphic base class, has an attribute with its unique identifier. Once an identifier is encountered the deserializer is looking at an Id->Type cache, or finds the class with the matching identifier within the types of all the loaded assemblies.

Lets imagine that the system is warming up, it starts by loading the essential modules (which are the assemblies the executable needs directly. Then, suddenly, a user sends a login request using a certain authentication method (which is not loaded, as it is not implemented in any of the main assemblies).

The authentication extension is not loaded, as it was never referenced, therefore, the deserialization would fail.

Is there an elegant way to solve this problem, without having to configure the plugins manually (I assume that if a plugin dll exists in a certain directory, it should be activated).

Thanks a lot!
Posted

1 solution

First of all, you should avoid doing it by any names, because it will require using string constants, which cannot be checked up by the compiler. This way, any solution using names will be really bad for maintenance.

The really good option is to define "plug-in interfaces" and implement it by plug-ins. In this approach, the reflection code of the host assembly looks for all or some types of a plug-in assemblies and checks up if the plug-in interfaces is implemented or not. If it is implemented, the implementing type is instantiated by the host and used through the interface reference (and only through the interface reference).

But I also used an additional technique to shorten up the search for implementing type(s). I also developed an assembly-level attribute which is used in a plug-in assembly and claims what types implement what plug-in interfaces. This is also reliable, because the attribute parameter can be of the type System.Type. However, .NET does not allow to discriminate types based on System.Type based on, say, base type (the missing feature is called "meta-classes", but this is not a place do discuss it seriously now). So, the host assembly loads a would-be-plug-in and looks at this attribute. If it is not present, this is not a plug-in. (If load was failed, this is not even a .NET assembly targeted to a compatible framework version or not a .NET assembly at all, so also not a plug-in.) Then, the types are checked up. If the said implementing type really implements the said interface, and if this interface is what is expected by the host, this is a valid plug-in. And then the implementing type is instantiated in a single step, without any search.

You can find other detail in my past answers:
Gathering types from assemblies by it's string representation[^],
C# Reflection InvokeMember on existing instance[^],
Dynamically Load User Controls[^].

This is all pretty simple. The complex part starts if, by some reason, you need your plug-ins to be not only loadable, but also unloadable. As .NET does not allow to unload an assembly for security reasons, separate Application Domains should be used, and working through the Application Domains boundary is much harder, requires IPC. If you have such situations, please ask a follow-up question, this is also a solvable problem, even somewhat harder one.

—SA
 
Share this answer
 
v2
Comments
ShacharK 19-Jul-13 11:28am    
Um, first of all thank you for your answer.

Secondly, I would like to ask you how do you see your suggestions combine with my design.

Lets review the design:

1. A serialized request object is received, we should deserialize it into a Composite Request.

There's the simpler case, The request class is defined within an assembly which was already loaded (statically).
2. Id property of the main object is examined.
3. A matching type is found (or is already cached, because it was previously used...)
4. The deserializer starts to populate the type. Lets say the Type was a registration request.

Here comes the tricker part (at least for me...).

The RegistrationRequest is composite and contains an abstract-class member "Registrator".
If an authentication plugin wants to expose a protocol interface,
it should implement few classes such as "Registrator", "Authenticator", etc.
That way, the run-time binding process that we're reviewing should do the connection.

We have an unloaded email plugin. Now the resolving algorithm, that worked fine with the straight-forward case
needs to know that the ID "12345678" means an EmailRegistrator. (We can assume that the registrator has an identifying attribute that says 12345678...) How does it happen?
Sergey Alexandrovich Kryukov 19-Jul-13 14:31pm    
For serialization, use Data Contract. It is based on reflection and System.Reflection.Emit, for performance. It stores and restore the whole object graph, even with circular references in it. Your concerns about "matching type found", etc. are related to polymorphism, which is also resolved with Data Contract. You need to mention all possible derived runtime types in the KnownTypeAttribute though, otherwise how the serialize would know then by the context of persistence stream (where those types are mentioned as tag names)?

At to the "tricky part", your explanation does not clearly explain the architecture you are trying to devise or use, but it does not look like a big problem. Perhaps you should use the technique similar to the "KnownTypeAttribute" I mentioned above (take a look at how it's done). I'm almost sure I could advise you in detain, if I had more information, but did you get the hint?

—SA
ShacharK 20-Jul-13 7:28am    
Of course I'm going to use standard serializers. I wrote my own JsonConverter that knows how to resolve my types, All I need to do is to translate the type of the member that is being populated. If it inherits some base class that has "Id" member, I query the "Id" value and translate it into a new type (I assert for assignability of course...). Then I simply populate the new type instead.

My architecture would look something like this:

Common:
Contains lots of stuff, mostly irrelevant. Mostly wrappers (Reflection, Cryptography wrappers) Also some Error Logging & Asserts, Debugging Utils).

Common.Serialization:
Contained within the Common.dll, exposes a BaseSerializer class, that all serializers must implement, along with the base type for the polymorphic serialization, the identification attribute and the resolver code, which can be used by inheriting Serializers.
Plugins.Serializers.Json: A separate DLL. Implements JsonSerializer (simple wrapper to an existing serializer).

Plugins.Communication.Protocol - Exposes a Command design pattern, with some basic classes, mostly dealing with authentication abstractly.

Plugins.Authentication.Email - Examples for a plugin that implement authentication logic using an email-address and password.
Plugins.Authentication.Email.Protocol - Implements the abstract classes needed for authentication by the forementioned protocol.

Use case in an Application:

The application is configured to use JsonSerializer along with the protocol.

It needs to know to resolve types from Plugins.Authentication.Email.Protocol lazily, if it exists. I just don't want the application to specify the modules it requires manually in any step (especially not the protocol which shouldn't be related to any project and technically be compiled once for several projects).

The only thing I could probably live with is loading the assemblies manually in my executable project, but I really prefer not to do so, as I'm trying to achieve a black-box framework that my project developers could use without touching my code at all, just write / add existing plugins wherever possible.

Thank you again :)
Sergey Alexandrovich Kryukov 20-Jul-13 22:13pm    
You should not write a serialize which knows your types. A serializer should be agnostic to types; this is the whole point of using reflection.

If you want JSON, use System.Runtime.Serialization.Json.DataContractJsonSerializer.

—SA
ShacharK 21-Jul-13 1:35am    
It knows only the base type, the rest is regular object population... I guess I might have re-implemented something, but it isn't the problem right now...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900