TinyIoC - Multiple Implementations of Interface - c#

I am just beginning to learn about IoC and Dependency Injection. I am planning on doing a MonoTouch project and wanted to use TinyIoC but I wanted to test it out first. I'm creating a dummy credit card processing console app and I'm having trouble with how to configure TinyIoC since I have multiple implementations of my interface. This is my test app.
Interface:
public interface IPaymentProcessor
{
void ProcessPayment(string cardNumber);
}
Two Implementations of the interface:
VisaPaymentProcessor
public class VisaPaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(string cardNumber)
{
if (cardNumber.Length != 13 && cardNumber.Length != 16)
{
new ArgumentException("Card Number isn't the correct length");
}
// some code for processing payment
}
}
AmexPaymentProcessor
public class AmexPaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(string cardNumber)
{
if (cardNumber.Length != 15)
{
new ArgumentException("Card Number isn't the correct length");
}
// some code for processing the payment
}
}
Simple stuff. Now I have a class that accepts the interface as a parameter in the constructor....
CreditCardProcessor
public class CreditCardProcessor
{
public IPaymentProcessor PaymentProcessor { get; set; }
public CreditCardProcessor(IPaymentProcessor processor)
{
this.PaymentProcessor = processor;
}
public void ProcessPayment(string creditCardNumber)
{
this.PaymentProcessor.ProcessPayment(creditCardNumber);
}
}
My console app looks like this....
class Program
{
static void Main(string[] args)
{
TinyIoCContainer.Current.AutoRegister();
var creditCardProcessor = TinyIoCContainer.Current.Resolve<CreditCardProcessor>();
creditCardProcessor.ProcessPayment("1234567890123456"); // 16 digits
}
}
So I am trying to figure out how to tell the Resolve which implementation of the interface to pass to the constructor. If I run this code, I will always use the VisaPaymentProcessor implementation.
So how can I make TinyIoC pass the AmexPaymentProcessor implementation to the constructor rather than the VisaPaymentProcessor(which seems to be the default)?

I haven't used TinyIoC myself, but I suspect you want:
TinyIoCContainer.Current.Register(typeof(IPaymentProcessor),
typeof(AmexPaymentProcessor));
(If you want to use Amex.)
There are various other Register overloads available, including one which takes a name to use, which may be useful when you resolve. It really depends on what you're trying to achieve, which isn't terribly clear from the question.

I'm not really sure what you're trying to achieve here, but if you have multiple implementations of an interface and you want a specific one then you need to register each one with a name, or use RegisterMultiple, which uses the type name for a name, then resolve using that name and use that along with NamedParameterOverloads to specify which one you want.
It sounds more like though that you might want some kind of ProcessorFactory, or a facade of some kind, that takes a dependency on IEnumerable and supplies/acts as a facade for the correct implementation depending on the number passed in.

Something like this in Global.asax or application entry (Modified for your example)
const string nameTrim = "paymentprocessor";
var type = typeof(IPaymentProcessor);
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(x => type.IsAssignableFrom(x) && x.IsClass).ToList()
.ForEach(t =>
{
var name = t.Name.ToLower();
if (name.EndsWith(nameTrim))
name = name.Substring(0, name.Length - nameTrim.Length);
TinyIoCContainer.Current.Register(type, t, name);
});
It finds alla implementations of IPaymentProcessor and registers them with classname
(-PaymentProcessor, if the classname ends with PaymentProcessor)
Then I can resolve for example "AmexPaymentProcessor" with
IPaymentProcessor handler;
if (TinyIoCContainer.Current.TryResolve("amex", out handler))
{
response = handler.ProcessPayment(cardNumber);
}

Related

Refactoring to make code open for extensions but closed for modifications

For my project purpose I need to send metrics to AWS.
I have main class called SendingMetrics.
private CPUMetric _cpuMetric;
private RAMMetric _ramMetric;
private HDDMetric _hddMetric;
private CloudWatchClient _cloudWatchClient(); //AWS Client which contains method Send() that sends metrics to AWS
public SendingMetrics()
{
_cpuMetric = new CPUMetric();
_ramMetric = new RAMMetric();
_hddMetric = new HDDMetric();
_cloudwatchClient = new CloudwatchClient();
InitializeTimer();
}
private void InitializeTimer()
{
//here I initialize Timer object which will call method SendMetrics() each 60 seconds.
}
private void SendMetrics()
{
SendCPUMetric();
SendRAMMetric();
SendHDDMetric();
}
private void SendCPUMetric()
{
_cloudwatchClient.Send("CPU_Metric", _cpuMetric.GetValue());
}
private void SendRAMMetric()
{
_cloudwatchClient.Send("RAM_Metric", _ramMetric.GetValue());
}
private void SendHDDMetric()
{
_cloudwatchClient.Send("HDD_Metric", _hddMetric.GetValue());
}
Also I have CPUMetric, RAMMetric and HDDMetric classes that looks pretty much similar so I will just show code of one class.
internal sealed class CPUMetric
{
private int _cpuThreshold;
public CPUMetric()
{
_cpuThreshold = 95;
}
public int GetValue()
{
var currentCpuLoad = ... //logic for getting machine CPU load
if(currentCpuLoad > _cpuThreshold)
{
return 1;
}
else
{
return 0;
}
}
}
So the problem I have is that clean coding is not satisfied in my example. I have 3 metrics to send and if I need to introduce new metric I will need to create new class, initialize it in SendingMetrics class and modify that class and that is not what I want. I want to satisfy Open Closed principle, so it is open for extensions but closed for modifications.
What is the right way to do it? I would move those send methods (SendCPUMetric, SendRAMMetric, SendHDDMetric) to corresponding classes (SendCPUMetric method to CPUMetric class, SendRAMMEtric to RAMMetric, etc) but how to modfy SendingMetrics class so it is closed for modifications and if I need to add new metric to not change that class.
In object oriented languages like C# the Open Closed Principle (OCP) is usually achieved by using the concept of polymorphism. That is that objects of the same kind react different to one and the same message. Looking at your class "SendingMetrics" it's obvious that the class works with different types of "Metrics". The good thing is that your class "SendingMetrics" talks to a all types of metrics in the same way by sending the message "getData". Hence you can introduce a new abstraction by creating an Interface "IMetric" that is implemented by the concrete types of metrics. That way you decouple your "SendingMetrics" class from the concrete metric types wich means the class does not know about the specific metric types. It only knows IMetric and treats them all in the same way wich makes it possible to add any new collaborator (type of metric) that implements the IMetric interface (open for extension) without the need to change the "SendingMetrics" class (closed for modification). This also requires that the objects of the different types of metrics are not created within the "SendingMetrics" class but e.g. by a factory or outside of the class and being injected as IMetrics.
In addition to using inheritance to enable polymorphism and achiving OCP by introducing the interface IMetric you can also use inheritance to remove redundancy. Which means you can introduce an abstract base class for all metric types that implements common behaviour that is used by all types of metrics.
Your design is almost correct. You got 3 data retriever and 1 data sender. So it's easy to add more metric (more retriever) (open for extensions) without affecting current metrics (closed for modifications), you just need a bit more refactor to reduce duplicated code.
Instead of have 3 metrics classes look very similar. Only below line is different
var currentCpuLoad = ... //logic for getting machine CPU load
You can create a generic metric like this
internal interface IGetMetric
{
int GetData();
}
internal sealed class Metric
{
private int _threshold;
private IGetMetric _getDataService;
public Metric(IGetMetric getDataService)
{
_cpuThreshold = 95;
_getDataService = getDataService;
}
public int GetValue()
{
var currentCpuLoad = _getDataService.GetData();
if(currentCpuLoad > _cpuThreshold)
{
return 1;
}
else
{
return 0;
}
}
}
Then just create 3 GetMetric classes to implement that interface. This is just 1 way to reduce the code duplication. You can also use inheritance (but I don't like inheritance). Or you can use a Func param.
UPDATED: added class to get CPU metric
internal class CPUMetricService : IGetMetric
{
public int GetData() { return ....; }
}
internal class RAMMetricService : IGetMetric
{
public int GetData() { return ....; }
}
public class AllMetrics
{
private List<Metric> _metrics = new List<Metric>()
{
new Metric(new CPUMetricService());
new Metric(new RAMMetricService());
}
public void SendMetrics()
{
_metrics.ForEach(m => ....);
}
}

Register Multiple Email Output Services using Simple Injector

I am using the excellent Simple Injector Ioc framework and would like to "plug in" multiple email output services ie Mandrill, MailChimp etc
My question really is am I doing this correctly as it results in a Cast at my send method.
So I have a simple IEmailOutputService
public interface IEmailOutputService
{
string Identifier { get; }
bool Send(object message, object contents);
}
And a MandrillOutputService (shortened)
public class MandrillOutputService : IEmailOutputService
{
public MandrillOutputService()
{
//DI stuff here
}
public string Identifier => "Mandrill";
public bool Send(EmailMessage message, IEnumerable<TemplateContent> templateContents)
{
if (message == null)
return false;
//send code here
return true;
}
bool IEmailOutputService.Send(object message, object contents)
{
//TODO this doesnt look right!!
var m = message as EmailMessage;
var c = contents as IEnumerable<TemplateContent>;
//forwards method onto bespoke Mandrill Send method above
return Send(m, c);
}
}
I have an EmailContext that gets the Email Output Provider for the logged in User eg "Mandrill" heres the IEmailContext
public interface IEmailContext
{
string GetProvider();
}
EmailOutputComposite is used to select correct Email Output Service
public class EmailOutputComposite : IEmailOutputService
{
private readonly IEmailContext _emailContext;
private readonly IEnumerable<IEmailOutputService> _emailOutputServices;
public string Identifier => "EmailOutputComposite";
public EmailOutputComposite(
IEmailContext emailContext, IEnumerable<IEmailOutputService> emailOutputServices)
{
this._emailContext = emailContext;
this._emailOutputServices = emailOutputServices;
}
bool IEmailOutputService.Send(object message, object contents) =>
this._emailOutputServices
.FirstOrDefault(x => x.Identifier.ToLower() == this._emailContext.GetProvider())
.Send(message, contents);
}
and finally registrations in Simple Injector
container.RegisterCollection(typeof(IEmailOutputService), new[]
{
typeof(MandrillOutputService)
//other emailOutputServices to go here
});
container.Register(typeof(IEmailOutputService), typeof(EmailOutputComposite),
Lifestyle.Singleton);
So my question is am I doing this correctly or is there a better way. I have to get the Users Email Provider (Mandrill) from Database so cant think of another way to do this but was concerned with the Cast I have to do in MandrillOutputService.Send method.
Wouldn't it be simpler to use the Strategy and Factory patterns, forgive me I'm going to change the implementation a bit:
For container registrations:
container.Register<EmailProviderFactory>(Lifestyle.Scoped);
container.Register<MandrillOutputService>(Lifestyle.Scoped);
container.Register<OtherOutputService>(Lifestyle.Scoped);
Then use a factory to resolve my email providers:
public class EmailProviderFactory
{
private readonly Container container;
public EmailProviderFactory(Container container)
{
this.container = container;
}
public IEmailOutputService Create(string provider)
{
switch (provider)
{
case "Mandrill": // should be in a constants class
return container.GetInstance<MandrillOutputService>();
case "Other": // should be in a constants class
return container.GetInstance<OtherOutputService>();
default: throw new ArgumentOutOfRangeException("provider");
}
}
}
I've changed the IEmailOutputService to have one method with explicit types:
public interface IEmailOutputService
{
bool Send(EmailMessage message, IEnumerable<TemplateContent> contents);
}
The email providers :
public class MandrillOutputService : IEmailOutputService
{
public bool Send(EmailMessage message, IEnumerable<TemplateContent> templateContents)
{
// ...
}
}
public class OtherOutputService : IEmailOutputService
{
public bool Send(EmailMessage message, IEnumerable<TemplateContent> templateContents)
{
// ...
}
}
Usage:
foreach(var userEmailProvider in UserEmailProviders) {
// I'm assuming the factory is injected
var emailService = _emailProviderFactory.Create(userEmailProvider.Name);
emailService.Send(new EmailMessage(), new List<TemplateContent>());
}
I do not think you need IEmailContext or a EmailOutputComposite. By using the EmailProviderFactory you will only create a specific provider when you need it.
I see two problems in your design:
You are violating the Liskov Substitution Principle in your MandrillOutputService by accepting only a subset of the accepted types of the IEmailOutputService abstraction; this might cause the appliation to break at runtime when the user supplies values that are invalid for that specific implementation.
The Identifier property on the IEmailOutputService violates the Interface Segration Principle, because it is a method that consumers don't use. The only class that is actually interested in this property is the EmailOutputComposite. Removing the Identifier from the abstraction has the advantage that it can simplify unit testing, since there is less code that a consumer can call. It also simplifies the interface, which is always a good thing.
I'm unsure how to fix the LSP principle, because its unclear to me how other implementations look like.
With respect to the ISP violation, you can do the following to fix it:
Mark the implementations instead with an attribute that defines their Identifier. This allows you to remove the property from the interface, but the downside is that the Composite can only filter those services in case the actual types are injected (and not decorated, because that disallows you from retrieving those attributes).
You let the Composite depend on the actual concrete implementations and implement a switch-case statement inside the Composite. This again allows you to remove the property from the interface, but downside is that you will have to update the composite every time a new implementation is added (which might not be that bad if you consider the Composite part of your Composition Root).
You define a dictionary of IEmailOutputServices during the registration process, where the Identifier is the dictionary's key. This removes the need to have the Identifier as part of the abstraction, but also removes the identifier from the implementation (which might actually be something good).
Here's an example of this last example:
container.RegisterSingleton<IEmailOutputService, EmailOutputComposite>();
container.RegisterSingleton(new Dictionary<string, Func<IEmailOutputService>>()
{
"Mandrill", CreateEmailServiceProducer<MandrillOutputService>(container),
"other", CreateEmailServiceProducer<Other>(container),
// ..
});
privte static Func<IEmailOutputService> CreateEmailServiceProducer<T>(Container c)
where T : IEmailOutputService =>
Lifestyle.Transient.CreateProducer<IEmailOutputService, T>(c).GetInstance;
Where the Composite is implemented as follows:
public class EmailOutputComposite : IEmailOutputService
{
private readonly IEmailContext _emailContext;
private readonly Dictionary<string, Func<IEmailOutputService>> _emailOutputServices;
public EmailOutputComposite(
IEmailContext emailContext,
Dictionary<string, Func<IEmailOutputService>> emailOutputServices)
{
_emailContext = emailContext;
_emailOutputServices = emailOutputServices;
}
public bool Send(object m, object c) => Service.Send(m, c);
IEmailOutputService Service => _emailOutputServices[_emailContext.GetProvider()]();
}
Whether or not this is actually an improvement is up to you.

NancyFx and TinyIoc

I am new to dependency injection.I am having problems understanding how to use the nancyFx bootstrapper and TinyIoc. I want to reference a dependency but can not understatnd how to do it. I can not seem to find the correct info anywhere or I am just misunderstanding it.This is my code:
public class Startup1
{
public void Configuration(IAppBuilder app)
{
app.UseNancy();
}
}
My interface:
public interface IPerson
{
string Getname();
}
My implementation:
public class Person : IPerson
{
public string Name { get; set; }
public string Getname()
{
return Name;
}
}
My program
static void Main(string[] args)
{
Person person = new Person();
person.Name = "Bill";
string uri = "http://localhost:8080/";
using (WebApp.Start<Startup1>(uri))
{
Console.WriteLine("Started");
Console.ReadKey();
Console.WriteLine("Stopping");
}
}
My nancy bootstrapper:
public class MyNancyBoot : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
//base.ConfigureApplicationContainer(container);
container.Register<IPerson>(new Person());
}
}
My nancyModule:
public class MyNancyModule : NancyModule
{
public MyNancyModule(IPerson person)
{
Get["/"] = _ =>
{
var x = TinyIoCContainer.Current.Resolve<IPerson>();
return "OK";
};
}
}
This return an error in MyNancyModule.
var x = TinyIoCContainer.Current.Resolve<IPerson>();
Ok so how do I get my person into MyNancyModule?
I want to reference a dependency but can not understatnd how to do it.
I think there are a couple of things to look at here.
You are mistakenly thinking that you need to somehow pass in, on the controller constructor, any type you may want to return from your Get["/"] controller operation. This is not the case. If one of the service operations happens to return a Person type, you do not need to inject that person type into the constructor. Rather you could just do something like:
Get["/"] = _ =>
{
var x = new List<Person>();
return Response.AsJson(x);
};
Obviously this service operation is of very limited use, as it will always return an empty list of type Person.
So what you need is a dependency which gets Persons. Eg:
interface IGetPersons
{
List<Person> GetAll();
}
In order for this to be used inside the controller, Tiny Ioc will need to know about it. So that is where you can then register this type with Tiny Ioc.
container.Register<IGetPersons>();
This brings us onto the second thing you are struggling with, which is how Tiny Ioc works.
After you add the implicit registration in the nancy bootstrapper, Tiny Ioc will then scan your assemblies and find the implementation of IGetPersons automatically! (This is assuming there is a single implementation. If more than one you will actually need to do more work but that's another story).
This magic then allows you to just modify your controller thus:
public MyNancyModule(IGetPersons personGetter)
{
Get["/"] = _ =>
{
var x = List<Person> personGetter.GetAll(); // Look, don't need to resolve this manually!
return Response.AsJson(x);
};
}
Tiny Ioc will take care of resolving the injected type to the IGetPersons implementation, which you are then free to use through the controller.
Lets say a client makes an http post to a win form app which is
running a nancy service. How do get the data from the nancy service to
the win form.
Have a look at the following: http://weblogs.asp.net/rweigelt/10235654
The author is using the Managed Extensibility Framework (MEF) to make changes to what I assume is some kind of singleton provided by the framework:
container.Register(MefContainer.GetExportedValue<IRemoteAccess>());

Arguments to ninject modules

I need to provide some global scope to a ninject module, and figure that it would be best to inject this in via a module.
I'm currently scanning assemblies for modules, and as such, can't determine how to provide these arguments (cleanly).
I have a console app, and I want to choose the implementation of a type based on the arguments to the application.
The issue is that those types have a dependency on the specific deserialisation of those arguments, which is determined at lookup time.
What I'd like to do is something like this:
public class Module : MyNinjectModule<Module>
{
private enum FictionalEnum
{
FirstType,
SecondType
}
private string[] _args;
private FictionalEnum Type
{
get
{
return IsFirstType(_args)
? FictionalEnum.FirstType
: FictionalEnum.SecondType;
}
}
public Module(string[] rawArgs)
{
_args = rawArgs;
}
protected override void LoadCustomBindings()
{
Bind<IBaseType>().To<FirstImpl>()
.When(req => this.Type == FictionalEnum.FirstType);
Bind<IBaseType>().To<SecondImpl>()
.When(req => this.Type == FictionalEnum.SecondType);
}
}
Does anyone have any suggestions for this?
In my opinion this logic does not belong into a module. I would either inject all the instances and then decide in the application which of the instances to take or inject a factory and let the factory create the correct type depending on the argument.

Problem with Order of "Registration" of .NET Classes in a Messaging Scenario

I've seen this problem come up a lot, but never adequately handled, and I haven't seen it on Stack Overflow, so here goes. I wish there were a way to put this shortly and succinctly without lacking clarity, but I can't seem to shorten it, so bear with me...
A good case-study (my current case, of course) to illustrate the problem follows:
I write code for many locations, a Parent Compary (parentco), and several satellite locations (centers). I have two 'Managers', one designed for the parentco, and one designed for the centers (deployed many times). I also have two libraries, one for the centers, and one generic library (that is used at the centers and the parentco), that programs can include to communicate to the appropriate Manager (via TCP). The library for the centers has several classes designed to wrap database tables and other 'Messages' to do other things, and the generic library has a few 'Messages,' too, such as 'end connection,' 'invoke a process,' and others.
The Question:
When the Manager recieves a Message that is defined in the 'generic' library, how can it know which type of message it is? The first-blush solution would be something like this:
namespace generic_library
{
public interface IMessage_Creator
{
public IMessage Create_Message(short id);
}
public interface IMessage
{
short Message_ID { get; }
}
/// <summary>Perhaps a message to kill the current connection</summary>
public class Generic_Message1 : IMessage
{
public short Message_ID { get { return ID; } }
internal const short ID = 1;
}
public static class Message_Handler
{
private static readonly System.Collections.Generic.List<IMessage_Creator> _creators =
new System.Collections.Generic.List<IMessage_Creator>();
public static void Add_Creator(IMessage_Creator creator)
{
_creators.Add(creator);
}
public static IMessage Get_Message(short id)
{
switch (id)
{//the Generic library knows about the generic messages...
case Generic_Message1.ID:
return new Generic_Message1();
}
//no generic message found, search the registered creators.
IMessage ret = null;
foreach (IMessage_Creator creator in _creators)
{
ret = creator.Create_Message(id);
if (ret != null)
{
return ret;
}
}
//null if no creator was found.
return ret;
}
}
}
namespace center
{
public class Center_Creator : generic_library.IMessage_Creator
{
static Center_Creator()
{
generic_library.Message_Handler.Add_Creator(new Center_Creator());
}
public generic_library.IMessage Create_Message(short id)
{
switch (id)
{//The center library knows about center-specific messages
case center_message1.ID:
return new center_message1();
}
//we return null to say, "I don't know about that message id."
return null;
}
}
public class center_message1 : generic_library.IMessage
{
public short Message_ID
{
get { return ID; }
}
internal const short ID = 2;
}
}
A little explanation. As you can see, the center and generic library have their own messages they can handle. The center interface (here represented by namespace center) registers his creator, Center_Creator, in the static constructor so when the Message_Handler gets a message of his type, the creator will be called on to generate the correct message.
The problem with this approach:
You may have already seen the problem here, and that is:
If the class Center_Creator is never accessed at all (one is never created, and a static method is never invoked) by code, which should be the case until a message of that type is recieved, the static constructor, static Center_Creator() is never invoked, so the Message_Handler never knows about this creator.
That's all fine and dandy, but I don't know how to fix it. Many people have suggested using reflection to invoke the Center_Creator Type Initializer, but I don't want to put that burden on every program that uses this library!
What is the Stack Overflow community's suggestion? Please let me know if I can simplify this to help make it more accessible for the community.
EDIT:
The code is for the generic library and the Center Library. As you can see, I will have the same issues with the Parent Company library.
A diagram of the architecture. http://cid-0676bb3c1f8d6777.office.live.com/self.aspx/Public/Manager.jpg
Image.
Let's break this down:
You have an application which is to send & receive certain types of messages.
A message type must be registered before it can be read, however,
You do not register the type until you send a message, but
You want to be able to read a message before you write one.
Clearly the answer is that you are registering your message types at the wrong time.
I would suggest an explicitly called Init() method for message types. This could be done by using reflection to scan the libraries to see would types are defined, or by manually listing them.
your message handlers can be seen as plugins which makes your problem a potential fit for the Managed Extensibility Framework. Since .Net 4 it's also shipped with the .Net framework.
You can find sample introductions to MEF here and here.
I've put together a litte example to show that it's quite simple to use basic MEF functionality (although there is much more you can do with it). First there is a PluginHost class which will host the plugins in its Plugins collection. Then there's a simple interface containing just the property Description and an example implementation of a plugin called ExamplePlugin.
The Plugins collection will be filled by the container.ComposeParts(..) method called in the constructor. All that's required to make that magic happen are the [Export] and [ImportMany] attributes.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace Playground
{
public class Program
{
static void Main(string[] args)
{
PluginHost host = new PluginHost();
host.PrintListOfPlugins();
Console.ReadKey();
}
}
public class PluginHost
{
[ImportMany]
public IEnumerable<IPlugin> Plugins { get; set; }
public PluginHost()
{
var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
public void PrintListOfPlugins()
{
foreach (IPlugin plugin in Plugins)
Console.WriteLine(plugin.Description);
}
}
public interface IPlugin
{
string Description { get; }
}
[Export(typeof(IPlugin))]
public class ExamplePlugin : IPlugin
{
#region IPlugin Members
public string Description
{
get { return "I'm an example plugin!"; }
}
#endregion
}
}
UPDATE: You can use so called Catalogs to discover plugins in more than one assembly. For example there is a DirectoryCatalog which gives you all exports found in all assemblies in a given directory.
AppDomain.CurrentDomain.GetAssemblies(); returns an array of all assemblies loaded into the current AppDomain. You could then iterate over that array to create an AggregateCatalog containing an AssemblyCatalog per loaded assembly.
Some ideas:
Use .NET serialization to serialize/deserialize your messages and put them in a class library used by both ends (or even use WCF to handle communication).
Add a custom attribute to your creator classes and populate the creator list using reflection at the first time Get_Message is called ("if (!initialized) FindAndAddCreators();").
Introduce some initialization method in your library that registers all the creator classes.
Try using a factory pattern.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace SO
{
class Program
{
static void Main(string[] args)
{
MessageFactory factory = new MessageFactory();
IMessage msg = factory.CreateObject(1);
IMessage msg2 = factory.CreateObject(2);
}
}
public interface IMessage
{
short Message_ID { get; }
}
public class Generic_Message1 : IMessage
{
public short Message_ID { get { return ID; } }
internal const short ID = 1;
}
public class center_message1 : IMessage
{
public short Message_ID { get { return ID; } }
internal const short ID = 2;
}
public class MessageFactory
{
private Dictionary<short, Type> messageMap = new Dictionary<short, Type>();
public MessageFactory()
{
Type[] messageTypes = Assembly.GetAssembly(typeof(IMessage)).GetTypes();
foreach (Type messageType in messageTypes)
{
if (!typeof(IMessage).IsAssignableFrom(messageType) || messageType == typeof(IMessage))
{
// messageType is not derived from IMessage
continue;
}
IMessage message = (IMessage)Activator.CreateInstance(messageType);
messageMap.Add(message.Message_ID, messageType);
}
}
public IMessage CreateObject(short Message_ID, params object[] args)
{
return (IMessage)Activator.CreateInstance(messageMap[Message_ID], args);
}
}
}
EDIT to answer comment:
If the "generic" library is the one processing the messages, and it has no knowledge of the types of message is is processing, you obviously have to change that.
Either move to a "plug-in" model where your custom message dlls will be loaded from a specific directory on startup of the generic library, or read the custom message dlls from a config file at startup for the generic library.
// Read customMessageDllName and customMessageClassName from your config file
Assembly assembly = Assembly.Load(customMessageDllName);
IMessage customMessage = (IMessage)assembly.CreateInstance(customMessageClassName);
Why not simply use WCF? You'll get ease of development, great support, as well as interoperability with Java.
Gallactic Jello is on the right path. The part he left out is overcoming the problem of the generic library knowing about classes in the center library, which I have further addressed. I've created a sample solution with three projects, the full contents of which I'll spare you. Here is the gist.
Class Library: Generic lib
Contains a Message_Handler, his own IMessage_Creator, definitions of the interfaces, and an IMessage type of his own.
Class Library: Center Lib
Contains an IMessage_Creator, and his own IMessage type.
Application: Application
has a SVM (static void Main()) containing the following lines of code:
Generic_lib.IMessage msg = Generic_lib.Message_Handler.get_message(2); //a Center Message
if (msg is Center_lib.Center_Message)
{
System.Console.WriteLine("got center message");
}
You will be amazed how important the if statement is!!! I'll explain later
Here's the code in the Type Initializer for Generic_lib.Message_Handler:
static Message_Handler()
{
//here, do the registration.
int registered = 0;
System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
foreach (System.Reflection.Assembly asm in assemblies)
{
System.Type[] types = asm.GetTypes();
foreach (System.Type t in types)
{
System.Type[] interfaces = t.GetInterfaces();
foreach (System.Type i in interfaces)
{
if (i == typeof(IMessage_Creator))
{
System.Reflection.ConstructorInfo[] constructors = t.GetConstructors();
foreach (System.Reflection.ConstructorInfo ctor in constructors)
{
if (ctor.GetParameters().Length == 0)
{
Add_Creator(ctor.Invoke(new object[0]) as IMessage_Creator);
registered++;
}
}
}
}
}
}
System.Diagnostics.Debug.WriteLine("registered " + registered.ToString() + " message creators.");
}
Horrific, isn't it? First, we get all the assemblies in the current domain, and here's where the if statement comes in. If there was no reference to the 'Center__lib' anywhere in the program, the array of Assemblies won't contain Center_lib. You need to be sure that your reference to it is good. Creating a method that is never called that references it is not enough, a using statement is not good enough,
if (msg is Center_lib.Center_Message) ;
is not enough. It has to be a reference that can't be optimized away. The above are all optimized away (even in Debug mode, specifying `don't optimize.'
I hope someone can come up with an even more elegant solution, but this will have to do for now.
Aaron

Categories