Using Unity to get objects based on name alone - c#

I have an app with a handler method. The handler method gets a json string which includes the name of the object that needs to handle the request and the parameters for the request. Basically, something (I'll keep it simple) like this:
public interface IJob
{
bool Execute();
bool Hydrate(string source);
}
public class JobBase
{
public int Id { get; set; }
public JobType JobType { get; set; }
public CronExpression CronExpression { get; set; }
}
public class JobSubmitClone : JobBase, IJob
{
public string[] Tokens { get; set; }
public bool Hydrate(string source)
{
// code omitted...
return true;
}
public bool Execute()
{
// code omitted...
return true;
}
}
IJob and JobBase are both kept in a Common class project. All apps reference this DLL.
In my main app I have Unity installed and one of the steps in loading the container is accomplished like:
// Scan assemblies for Job definitions...
_container.RegisterTypes(AllClasses.FromAssembliesInBasePath().
Where(type => typeof(IJob).IsAssignableFrom(type)),
WithMappings.FromAllInterfaces,
WithName.TypeName,
WithLifetime.Transient);
Each "Job" is defined in its own class project and is NOT referenced by the main app. Each "Job" must inherit from JobBase and IJob.
The main app has a simple REST service exposed. You can post something like:
{ jobName : JobSubmitClone, Id : 1, JobType : 2, CronExpression : '' }
In the main app I am trying to pull the object from the container based on JobName. I've tried this (yes, I know it violates the IoC pattern):
var container = UnityHelpers.GetConfiguredContainer();
var job = container.Resolve<IJob>(myParams.jobName); // "JobSubmitClone" //
var hydrated = job.Hydrate(myParams);
if(hydrated)
var result = job.Execute();
I am getting the following error:
Exception is: InvalidOperationException - The current type, IJob, is
an interface and cannot be constructed. Are you missing a type
mapping?
What am I missing?

Each "Job" is defined in its own class project and is NOT referenced
by the main app. Each "Job" must inherit from JobBase and IJob.
Have you looked into MEF? It has the ability to query and load a class by its metadata. I tend to use Unity for known, compile-time dependencies and MEF for dynamic assemblies loaded at runtime. (There's no reason you can't use both in the same project.)
We do something similar to what you're looking for, I think. We load workflows based on their class name.
Just decorate the Job with a System.ComponentModel.Composition.MetadataAttribute....
[MetadataAttribute]
public class WorkflowMetadataAttribute : Attribute, IWorkflowMetadata
{
public WorkflowMetadataAttribute(string typeName) {
TypeName = typename;
}
public string TypeName { get; private set; }
}
Which you put on the thing you want to export....
public interface IWorkflow // aka IJob
{
void Execute();
}
[Export(typeof(IWorkflow))]
[WorkflowMetadata("WhateverWorkflow")]
public class WhateverWorkflow : IWorkflow
{
public void Execute() { }
}
The exported class can be built separately of the project that runs it. If you build it as a library into a separate assembly, you can load the assembly (or directory of assemblies) in an importer class.
public class WorkflowCatalog : IPartImportsSatisfiedNotification
{
[ImportMany]
public IEnumerable<Lazy<IWorkflow, IWorkflowMetadata>> Workflows { get; private set; }
public void Compose() {
var path = Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location );
var catalog = new DirectoryCatalog( path );
var compositionContainer = new CompositionContainer( catalog );
compositionContainer.ComposeParts(this);
}
public void OnImportsSatisfied() {
var workflow = Workflows.Single(w => w.Metadata.TypeName == "WhateverWorkflow").Value;
workflow.Execute();
}
}
IJob, IJobMetadata, and JobBase live in the core. Job classes live in their own libraries (or they can live in the main program too I suppose).

Turns out that there are a lot of ways to manipulate Unity. This is what ended up working so far:
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(type => typeof(IJob).IsAssignableFrom(type) && type.IsClass),
WithMappings.FromAllInterfaces,
t => t.IsNested ? t.DeclaringType.Name + "." + t.Name : t.Name,
WithLifetime.Transient);
I also built an extension method:
public static IJob Job(this string src)
{
var container = UnityConfig.GetConfiguredContainer();
var job = container.Resolve<IJob>(src);
return job;
}
I created a small model for the Minimum Payload:
public class MinimumCommandModel : IRequest<MinimumResultModel>
{
public MinimumCommandModel(string json)
{
FullPayloadString = json;
MinimumPayload = JsonConvert.DeserializeObject<MinimumPayload>(json);
}
public string MinimumPayloadString => JsonConvert.SerializeObject(MinimumPayload);
public string FullPayloadString { get; set; }
public MinimumPayload MinimumPayload { get; set; }
}
I can then directly get a job from a (JSON) sting payload:
var command = new MinimumCommandModel(Request.Content.ReadAsStringAsync().Result);
var job = command.MinimumPayload.JobName.Job();

Related

Custom IOptions map path

I currently have a Json config file that looks something like this :
{
"MySettings" " {
"SomeSetting" : "SomeValue"
}
}
In the perfect world, I would have a class that matches that same structure. But, I need to map it to a class that would look something like this :
public class MySettingsUpdated
{
public string MyRenamedSetting {get;set;}
}
I am already using a custom ConfigurationProvider to get data from a configuration file (for various reasons), and I -could- create the data in the expected path in there, but it would make my life much easier if I could decorate the new class with some type of attribute in order to specify where the data needs to come from.
Any way to do this?
static string data = #"
{
""class"": {
""property"" : ""some string!""
}
}";
class DTO
{
[JsonProperty("class")]
public Data Property { get; set; }
}
class Data
{
[JsonProperty("property")]
public string Value { get; set; }
}
static void Main(string[] args)
{
var result = JsonConvert.DeserializeObject<DTO>(data);
}
You can use the .Bind() method exposed on the IConfigurationSection interface.
In a class it could look like
public class SomeClassDoingWork
{
private MyConfigClass MyConf = new MyConfigClass();
public SomeClassDoingWork(IConfiguration config)
{
config.GetSection("MySettings").Bind(MyConf);
}
}

Howto inject the right instance of a single type into another class

I've got two typed HttpClients that get a configuration class injected:
public class MyConfig
{
prop string Prop { get; set; }
}
public class MyConfig2
{
prop string Prop { get; set; }
}
public class HttpClientService
{
public HttpClientCoreService(HttpClient client, IOptions<MyConfig> config)
{
}
}
public class HttpClientService2
{
public HttpClientCoreService2(HttpClient client, IOptions<MyConfig2> config)
{
}
}
As you can see the classes are basically identical, just the configuration isn't.
services.Configure<MyConfig>(x =>
{
x.Prop = "x";
}
services.Configure<MyConfig2(x =>
{
x.Prop = "y";
}
services.AddHttpClient<HttpClientService>();
services.AddHttpClient<HttpClientService2>();
I'd like to have just one config and one service but therefore I need some DI magic (if possible) to happen.
I somehow need to tell the DI which version of MyConfig should be injected for the specific instance of HttpClientService (this should be possible if I use named HttpClients).
Is this possible?

c# instantiate mapping class by name

I have standard XML data coming in that represents a purchase order from a customer. Each customer will populate the XML data differently so I need a separate method to process the order based on their specifications. My goal is to make this scalable so I used an interface because I would like to be able to create additional classes as new customers are added.
How do I select a different Map class based on the customer?
public class XmlPurchaseOrder
{
public DateTime Created { get; set; }
public string CustomerId { get; set; }
public string PurchaseOrderId { get; set; }
public string MapName { get; set; }
//...
}
public interface IXmlMapper
{
CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po);
}
public class CustomerOrder
{
public int Id { get; set; }
public string CustomerId { get; set; }
public string CustomerPoId { get; set; }
public DateTime OrderDate { get; set; }
}
//Maps by customer
public class McClownMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "McD123",
CustomerPoId = po.PurchaseOrderId,
OrderDate = DateTime.Today
};
}
}
public class BkMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "BxK331",
CustomerPoId = string.Format("BxK{0}", po.PurchaseOrderId),
OrderDate = DateTime.Today.AddDays(-1)
};
}
}
public class TacoWorldMap : IXmlMapper
{
public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
{
return new CustomerOrder()
{
CustomerId = "TW-33",
CustomerPoId = string.Format("{0}-{1}",po.PurchaseOrderId, DateTime.Now.Ticks),
OrderDate = po.Created
};
}
}
class Program
{
private static void Main(string[] args)
{
const string xmlFile = "CustomerPo.xml";
var objStreamReader = new StreamReader(xmlFile);
var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
objStreamReader.Close();
//How do I create the associated class by the MapName specified.
IXmlMapper t = Activator.CreateInstance(Type.GetType(po.MapName));
var customerOrder = t.MapToCustomerOrder(po);
//...
}
}
Thanks
Perhaps you could split the workload, so that your Deserializer decorates the XmlPurchaseOrder with a PurchaseOrderType (enum) based on the characteristics that determines the purchase order type. If this is determined by the XML structure itself, like via a tag or an attribute, this is a simple task - otherwise subclass the XmlPurchaseOrder and introduce a virtual method that "calculates" the type.
The other part of the job is to instantiate the concrete PurchaseOrder - this can be simplified using a Factory with one Create method for each kind of purchase order, or more brute force with a big switch on the PurchaseOrderType enum.
A very simple way would be to add a config setting for each customer that maps to the type used to process their order.
<appSettings>
<add key="Customer1" value="MyApp.Logic.Customer1Processor" />
<add key="Customer2" value="MyApp.Logic.Customer2Processor" />
//etc...
</appSettings>
then use Activator.CreateInstance like you have currently.
This makes me think of the Provider Model available through .Net. I am currently using it to instantiate different API Providers based on their Provider Type.
You can set up a near infinite number of different classes that inherit from ProviderBase and add whatever methods you will need to this class. Then, you create each .dll to perform whatever functionality you need and since they have all inherited from some similar base class, you can put the primary method to begin processing the functionality in there.
Base class:
namespace ProviderManager
{
abstract public class SendProviderBase : ProviderBase
{
abstract public void Process(whatever args you need);
}
}
Helper class used to instantiate different Providers
namespace ProviderManger
{
public class ProviderManger
{
private ConfigHandler sendConfig;
public ProviderManger()
{
sendConfig = ConfigurationManger.GetSection("sendProvider") as ConfigHandler;
}
public SendProviderBase GetSendProviderBase(string MapName)
{
try
{
ProviderSettings settings = sendConfig.Providers[MapName];
return (SendProviderBase)ProvidersHelper.InstantiateProvider(settings, typeof(SendProviderBase));
}
//appropriate catch block and whatever else
}}
ConfigHandler code
namespace ProviderManger
{
class ConfigHandler : ConfigurationSection
{
[ConfigurationProperty("providers")}
public ProviderSettingsCollection Providers
{
get
{ return base["providers"] as ProviderSettingsCollection; }
}}}
Usage in Main for you
providerManager = new ProviderManager();
SendProviderManger provider = providerManager.GetSendProviderBase(MapName);
provider.Process(whatever args...);
Obviously you could rename SendProviderBase to something more related to what you're doing but I kept that name since it was consistent through my code here. The only other thing you'll need is a declaration of the .config section used to store MapNames that map to the .dll that is related to it. Since my application is a web service we have a web.config with the following sections:
Custom Section declaration:
<configSections>
<section name="sendProvider" type="KC.ProviderManager.ConfigHandler, ProviderManager"/>
</configSections>
And the Send Provider section:
<sendProviders>
<providers>
<add name="MapNameX" type="namespace.classname, assemblyname">
So basically what this does is you feed providerManger.GetSendProviderBase(MapNameX) the name in the web.config and it returns to you (assuming everything else is built correctly) the class found in that assembly. Then you can call the method found on the base class to begin processing (provider.Process()).
The other necessary References are as follows
System.Reflection;
System.Configuration;
System.Configuration.Provider;
System.Web.Configuration;
This is highly scalable as you can add as many providers as you want as long as they inherit correctly
Or, for a more simplified but still quite scalable solution similar to this check out this link
I did some further research and what I needed was a Factory. This is my interpretation of a demo in a Pluralsight.com video called Design Patterns Library that was presented by David Starr
public class CustomerMapFactory
{
private Type[] _mapTypes;
public CustomerMapFactory()
{
LoadAvailableMaps();
}
//Return a newly created Type
public IXmlMapper CreateInstance(string customerId)
{
var t = GetTypeToCreate(customerId);
if (t == null) throw new Exception("Customer map not found");
return Activator.CreateInstance(t) as IXmlMapper;
}
//Find the map to instantiate
Type GetTypeToCreate(string customerId)
{
return _mapTypes.FirstOrDefault(tpMap => tpMap.Name.Contains(customerId));
}
//Identify all Types that use the IXmlMapper
private void LoadAvailableMaps()
{
_mapTypes = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.GetInterface(typeof(IXmlMapper).ToString()) != null)
.ToArray();
}
}
}
Here is the program that utilizes the factory
class Program
{
private static void Main(string[] args)
{
//Same as above
const string xmlFile = "CustomerPo.xml";
var objStreamReader = new StreamReader(xmlFile);
var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
objStreamReader.Close();
//Now utilizing the factory.
var mf = new CustomerMapFactory();
var poMap = mf.CreateInstance("BkMap");
var customerOrder = poMap.MapToCustomerOrder(po);
}

Finding difficulty in unit testing a Controller Action method using NUnit

I want to Unit test the method ExecLogin() using NUnit testing framework. But I'm having problem while writing code for the testing because the GenerateInput() method is declared as internal virtual. So, I'm not being able pass the SessionUserId parameter to it which I think is needed. Please guide me how to write the unit test code for ExecLogin(). I'm new to testing.
This is related to ASP.NET MVC3 testing controller actions.
public class BaseController : Controller
{
public string SessionUserId
{
get { return Session["UserId"] as string; }
set { Session["UserId"] = value; }
}
internal virtual SomeType GenerateInput(SomeType inputData)
{
var finalInput = new StringBuilder(600);
finalInput.Append("<Root>")
.Append("<UserId>")
.Append(SessionUserId)
.Append("</UserId>")
.Append("<IPAddress>")
.Append(this.GetClientIpAddress())
.Append("</IPAddress>")
.Append(inputData.InputValue)
.Append("</Root>");
inputData.InputValue = finalInput.ToString();
return inputData;
}
}
public class LoginController : BaseController
{
[HttpPost]
public JsonResult ExecLogin(JsonRequest<LoginModel> request)
{
var loginObj = request.InputObject;
var login = new StringBuilder(300);
login.Append("<UserName>").Append(loginObj.UserName).Append("</UserName>")
.Append("<Password>").Append(EncodingHelper.GetHashedPassword(loginObj.Password)).Append("</Password>")
.Append("<RememberMe>").Append(loginObj.RememberMe).Append("</RememberMe>")
;
var input = new SomeType { Operation = request.Operation, PageName = ActionName, InputValue = login.ToString() };
var returnValue = SomeDelegate.Instance.ExecuteOperation(GenerateInput(input));
return Json(new JsonResponse<string> { Status = returnValue.Status, Result = this.PopulateSession(returnValue) });
}
}
This is our model classes:
public class LoginModel
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool RememberMe { get; set; }
public bool IsValidUser { get; set; }
}
public class JsonRequest<T>
where T : class
{
public string Operation { get; set; }
public T InputObject { get; set; }
}
While learning Dependency Injection pattern I came to know that use of concrete classes makes code more tightly coupled. And the more tightly coupled the code the harder it becomes to test. Is this why I'm not being able to test it properly? Please help.
This is actually part of a common pattern, how to provide additional hook points for testing without disturbing the interface that is publicly available, and fortunately internal is your friend here.
I assume the problem is that you have 2 assemblies, let's call them MyCode.dll and MyCode.Tests.dll for simplicity. This means that MyCode.Tests cannot access the internal methods of MyCode because, well basically it isnt internal :-)
If you go to your AssemblyInfo.cs file for your MyCode project, you can add the following
[assembly:InternalsVisibleTo("MyCode.Tests")
This does exactly what it says, it makes your internals visible inside the MyCode.Tests code base. For more info see InternalsVisibleTo and Friend assemblies

How to extend mef using custom attributes?

Is it possible to add some additional attributes to my components which are then set/hydrated using some custom logic/perhaps from a data store? Similar to adding some custom builder strategy in cab/unity ?
UPDATE
e.g.
assuming a class has these properties
[MyImport] string name1 { get; set }
[MyImport] MyType name2 { get; set }
[MyGuid] Guid { get; set; }
with custom attributes MyImport and MyGuid which are resolved by an "extension" to MEF ( which gets executed after the [imports] are resolved ) and has code along these lines
// property SET
var valu = myDBStore.GetValue( instanceGUID, propertyInfo.Name);
propertyInfo.SetValue( instance, TypeDescripter.GetConverter(valu).ConvertTo(propertyType), null);
// property GET - for example only, used during dehydration outside of MEF !
var valu = propertyInfo.GetValue( instance, null);
myDBStore.SetValue( instanceGUID, propertyInfo.Name, TypeDescripter.GetConverter(valu).ConvertTo(typeof(string));
// the above is pseudo code only, pls no comments on correct args/syntax :)
EDIT
components which are then set/hydrated using some custom logic/perhaps from a data store
One can do this via an "ExportFactory".
// "ExportFactory"
public sealed class DataStoreProvider
{
[Export(typeof(Model))]
public Model Item
{
get
{
return [custom logic];
}
}
}
public class NeedsModel
{
[Import(typeof(Model))]
public Model Item { get; set; }
}
Initial Answer
This is possible through MEF's Lazy<T, TMetadata>.
public interface ISomeMetadata
{
string UsefulInfo { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ExportBaseAttribute : ExportAttribute, ISomeMetadata
{
public ExportBaseAttribute(string usefulInfo)
:base(typeof(BaseExport))
{
UsefulInfo = usefulInfo;
}
public string UsefulInfo { get; private set; }
}
// BaseExport class is not needed.. just showing advanced attribute usage.
public abstract class BaseExport { }
[ExportBase("Useful Filter Information")]
public class SomeExport : BaseExport
{
}
Then, in your host (composer), you can
[ImportMany(typeof(BaseExport))]
Lazy<BaseExport, ISomeMetadata>[] _baseExports
After you compose, you can run a LINQ filter using .Metadata
var goodExports = from export in _baseExports
where export.Metadata.UsefulInfo ...
select export;

Categories

Resources