Castle Windsor Resolving Mutiple Generic Implementations of 1 Inteface - c#

Suppose I have the following:
public interface IFileHandler<TLocation,TRow> { ... }
//loads file in some location using record format defined in RowTypeA
//e.g. RowTypeA records exist in files that match string 'file_a.csv'
public class FileHandlerA<TLocation> : IFileHandler<TLocation, RowTypeA>{ ... }
//loads file in some location using record format defined in RowTypeB
//e.g. RowTypeB records exist in files that match string 'file_b.csv'
public class FileHandlerB<TLocation> : IFileHandler<TLocation, RowTypeB>{ ... }
public class MyCode {
...
public void MyMethod()
{
//here is how I would like to resolve file handlers
var fh1 = container.Resolve<IFileHandler<Location1, RowTypeA>>(); //resolves to FileHandlerA<Location1>
var fh2 = container.Resolve<IFileHandler<Location2, RowTypeA>>(); //resolves to FileHandlerA<Location2>
var fh3 = container.Resolve<IFileHandler<Location1, RowTypeB>>(); //resolves to FileHandlerB<Location1>
}
...
}
How do I do the registration in Castle Windsor for this? Or is there a better design for this problem?
P.S.
I tried:
Classes
.FromAssembly(thisAssembly)
.BasedOn(typeof(IFileHandler<,>)
.WithServiceAllInterfaces();
Also thought about this:
Component
.For<IFileHandler<,>()
.ImplementedBy<???>

I think this should work but haven't tested it:
var locationTypes = typeof(ILocation).Assembly.GetTypes()
.Where(t => !t.IsInterface && !t.IsGenericType).ToArray();
var fileHandlerTypes = typeof(IFileHandler<,>).Assembly.GetTypes()
.Where(t => !t.IsInterface && !t.IsGenericType && t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFileHandler<,>))).ToArray();
foreach (var fh in fileHandlerTypes)
foreach(var locType in locationTypes)
{
var rowType = fh.BaseType.GenericTypeArguments[1];
var requestedType = typeof(IFileHandler<,>).MakeGenericType(locType).MakeGenericType(rowType);
container.Register(Component.For(requestedType, fh));
}

Related

NHibernate 4.0 code mapping with intentional duplicate import

How can I get this working using only NHibernate 4.0 and CoC?
I need to map two different classes that share the same name:
namespace MyApp.VersionA {
public class User{
//omitted properties
}
}
namespace MyApp.VersionB {
public class User{
//omitted properties
}
}
This is my NHibernate setup method:
var config = new Configuration();
config.Configure();
var mapper = new ModelMapper();
mapper.AddMappings(GetAllMappingTypes());
config.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
config.BeforeBindMapping += (sender, args) => args.Mapping.autoimport = false;
Factory = config.BuildSessionFactory();
Notice that I set up autoimport=false, but I still get an DuplicateMappingException from NHibernate:
nhibernate.duplicatemappingexception: duplicate import:
User refers to both
MyApp.VersionA,
... and
MyApp.VersionB.User,
... (try using auto-import="false")
Alexander, try this:
var assemblies =
AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name.Contains(".Infrastructure"));
foreach (var assembly in assemblies)
{
var mapper = new ModelMapper();
mapper.AddMappings(assembly.GetExportedTypes()
.Where(t => t.BaseType != null && t.BaseType.IsGenericType &&
t.BaseType.GetGenericTypeDefinition() == typeof (ClassMapping<>)));
var compileMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
compileMapping.autoimport = false;
configuration.AddMapping(compileMapping);
}

Static Query Building with NEST

I'm playing around with Elasticsearch and NEST.
I do have some trouble understanding the various classes and interfaces which can be used to create and build static queries.
Here's a simplified example of what I want to achieve:
using Nest;
using System;
using System.Text;
namespace NestTest
{
public class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
public class ProductFilter
{
public string[] IncludeNames { get; set; }
public string[] ExcludeNames { get; set; }
public int MaxPrice { get; set; }
}
class Program
{
static void Main(string[] args)
{
var filter = new ProductFilter();
filter.MaxPrice = 100;
filter.IncludeNames = new[] { "Notebook", "Workstation" };
filter.ExcludeNames = new[] { "Router", "Modem" };
var query = CreateQueryFromFilter(filter);
var client = new ElasticClient();
// Test Serialization
var serialized = Encoding.UTF8.GetString(client.Serializer.Serialize(query));
Console.WriteLine(serialized);
// TODO: How to convert the IQuery to QueryContainer?
//client.Search<Product>(s => s.Query(q => query));
}
private static IQuery CreateQueryFromFilter(ProductFilter filter)
{
var baseBoolean = new BoolQueryDescriptor<Product>();
if (filter.IncludeNames != null && filter.IncludeNames.Length > 0)
{
foreach (var include in filter.IncludeNames)
{
// TODO: This overwrites the previous must
baseBoolean.Must(q => q.Term(t => t.Name, include));
}
}
if (filter.ExcludeNames != null && filter.ExcludeNames.Length > 0)
{
foreach (var exclude in filter.ExcludeNames)
{
// TODO: This overwrites the previous must
baseBoolean.MustNot(q => q.Term(t => t.Name, exclude));
}
}
if (filter.MaxPrice > 0)
{
// TODO: This overwrites the previous must
baseBoolean.Must(q => q.Range(r => r.LowerOrEquals(filter.MaxPrice).OnField(f => f.Price)));
}
return baseBoolean;
}
}
}
As you can see, I'd like to create some kind of query object (most likely BoolQuery) and then fill this object later on. I've added some TODOS in code where I have the actual problems. But in general, there are just too many possibilities (IQuery, QueryContainer, XXXQueryDescriptor, SearchDescriptor, SearchRequest) and I cannot figure out how to successfully "build" a query part by part.
Anybody who could enlighten me?
Combinding boolean queries is described in the documentation here:
http://nest.azurewebsites.net/nest/writing-queries.html
That page is slightly outdated and will be updated soon although most of it still applies I updated your CreateQueryFromFilter method to showcase the several ways you can formulate queries:
private static IQueryContainer CreateQueryFromFilter(ProductFilter filter)
{
QueryContainer queryContainer = null;
if (filter.IncludeNames != null && filter.IncludeNames.Length > 0)
{
foreach (var include in filter.IncludeNames)
{
//using object initializer syntax
queryContainer &= new TermQuery()
{
Field = Property.Path<Product>(p => p.Name),
Value = include
};
}
}
if (filter.ExcludeNames != null && filter.ExcludeNames.Length > 0)
{
foreach (var exclude in filter.ExcludeNames)
{
//using static Query<T> to dispatch fluent syntax
//note the ! support here to introduce a must_not clause
queryContainer &= !Query<Product>.Term(p => p.Name, exclude);
}
}
if (filter.MaxPrice > 0)
{
//fluent syntax through manually newing a descriptor
queryContainer &= new QueryDescriptor<Product>()
.Range(r => r.LowerOrEquals(filter.MaxPrice).OnField(f => f.Price)
);
}
return queryContainer;
}
Here's how you can pass that to a search operation:
static void Main(string[] args)
{
//using the object initializer syntax
client.Search<Product>(new SearchRequest()
{
Query = query
});
//using fluent syntax
client.Search<Product>(s => s.Query(query));
}

Automated testing the EF model-building process

I'm doing some complex modifications to our EF model (we're using code-first) and I know it should affect my database in particular and testable ways; for instance, I should be able to assert;
The db will have an 'Item' table
'Item' will have an integer 'Id' field which is the primary key
'Item' will have a 50-character string 'Name' field
These are ripe for unit testing, but I can't find a way to test these kinds of assertion.
What I'd like is a way to get the database model from the DbContext, so that I can do something like;
var model = BuildModel(MyEntityContext);
var itemTable = model.Db.Tables["Item"]
Assert.IsNotNull(itemTable);
Assert.IsTrue(itemTable.Columns["Id"] != null);
Assert.IsTrue(itemTable.Columns["Name"] != null);
Assert.IsTrue(itemTable.Columns["Name"].Length == 50);
...
Does anyone know of a way to get hold of a description of the database structure implied by a DbContext object?
you could start with something like this...see if this takes you anywhere.
using(ObjectContext context = new ObjectContext())
{
var queryResult = from meta in context.MetadataWorkspace.GetItems(DataSpace.CSpace)
.Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
from p in (meta as EntityType).Properties
.Where(p => p.DeclaringType.Name == context.GetType().Name
&& p.Name == PropertyName
Select new {Length = p.TypeUsage.Facets["MaxLength"].Value, Name=p.TypeUsage.Facets["Name"].Value, p.TypeUsage.Facets["FacetType"].Value
}
(#steve-cooper) A really helpful start! thanks. I've come up with a working class based closely on your suggestion -- I've stuck it here so I can give you the accepted answer, and so others can see the worked-up code.
public class ModelSummary
{
public Dictionary<string, System.Data.Metadata.Edm.EntityType> Entities { get; private set; }
public static ModelSummary Load(DbContext context)
{
var adapter = (IObjectContextAdapter)context;
var objectContext = adapter.ObjectContext;
var summary = new ModelSummary();
var items = objectContext.MetadataWorkspace.GetItems(DataSpace.SSpace);
summary.Entities =
objectContext.MetadataWorkspace.GetItems(DataSpace.SSpace)
.OfType<EntityType>()
.ToDictionary(et => et.Name);
return summary;
}
public bool EntityExists(string entityName)
{
return this.Entities.ContainsKey(entityName);
}
public bool EntityHasProperty(string entityName, string propertyName)
{
if (!EntityExists(entityName))
{
return false;
}
var entity = this.Entities[entityName];
return entity.Properties.Contains(propertyName);
}
}
And the test looks like this;
[Test]
public void Context_SchemaIsExpected()
{
var summary = ModelSummary.Load(new MyContext());
Assert.IsTrue(summary.EntityExists("Item"));
Assert.IsTrue(summary.EntityHasProperty("Item", "Id"));
}

Find all classes which derive from a specific base class and add them to the registry

I have a base class called BaseStatus which looks like this:
public class BaseStatus
{
public int UnitId { get; protected set; }
public UInt16 StatusValue { get; protected set; }
public string StatusCode { get; protected set; }
public string StatusDescription { get; protected set; }
public BaseStatus()
{
this.UnitId = -1;
this.StatusValue = 0;
this.StatusCode = null;
this.StatusDescription = null;
}
}
Furthermore i have two or more other base classes which derive from BaseStatus and define a other unit id. For example the two classes
public class BaseGlobalStatus : BaseStatus
{
public BaseGlobalStatus()
{
base.UnitId = -1;
}
}
public class BaseGcmGdmStatus : BaseStatus
{
public BaseGcmGdmStatus()
{
base.UnitId = 2;
}
}
public class BaseCcuStatus : BaseStatus
{
public BaseCcuStatus()
{
base.UnitId = 1;
}
}
The Background is that i want to derive from for example BaseCcuStatus and have the correct UnitId in the derived class.
Now i define my correct status classes for example:
public class StatStErrDefinition : BaseGlobalStatus
{
public StatStErrDefinition()
: base()
{
base.StatusDescription = "Kommando nicht zulässig, unit im state ERROR";
base.StatusCode = "STAT_ST_ERR";
base.StatusValue = 3;
}
}
public class GcmStErrDefinition : BaseGcmGdmStatus
{
public GcmStErrDefinition()
: base()
{
base.StatusDescription = "Kommando nicht zulässig, unit im state ERROR";
base.StatusCode = "STAT_ST_ERR";
base.StatusValue = 3;
}
}
public class CcuStErrDefinition : BaseCcuStatus
{
public CcuStErrDefinition()
: base()
{
base.StatusDescription = "Kommando nicht zulässig, unit im state ERROR";
base.StatusCode = "STAT_ST_ERR";
base.StatusValue = 3;
}
}
For my understading, the three classes StatStErrDefinition, GcmStErrDefinition and CcuStErrDefinition should have the UnitId which is set in the derived BaseClass?
Now that i have defined my three Status Classes i want to get them into a registry. Currently im using this piece of code to try get them. Problem is that the result has no items.
registry = new StatusDictionary<UInt16, BaseStatus>();
var unitStatus = typeof(BaseStatus)
.Assembly.GetTypes()
.Where(x => x.BaseType == typeof(BaseStatus))
.Select(x => new
{
StatusType = x,
UnitId = x.GetProperty("UnitId", BindingFlags.Public)
StatVal = x.GetProperty("StatusValue", BindingFlags.Public)
}
)
.Where(x => x.StatVal != null && x.UnitId != null)
.Select(x => new
{
UnitId = (int)x.UnitId.GetValue(null, null),
StatusValue = (UInt16)x.StatVal.GetValue(null, null),
Factory = (Func<BaseStatus>)(() => ((BaseStatus)Activator.CreateInstance(x.StatusType)))
});
try
{
foreach (var status in unitStatus)
{
if (status.UnitId == unitId
|| status.UnitId < 0)
registry.Register(status.StatusValue, status.Factory);
}
}
catch (Exception ex)
{
string temp = ex.Message;
}
After the LINQ expression the var unitStatus is empty...
Later, the registry call looks like that to get the specific class but that is unimportant at this point:
stat = StatusContainer.GetRegistry(this.unitTypeId).GetInstance(this.StatusValue);
For information:
I want to get the status class which should be in the registry by the unittypeid and the specific status value.
Currently my registry method does not work because he is not able to find any class. So there has to be a mistake somewhere. Thanks in advance
#Update 1
I changed my functionality a little bit:
registry = new StatusDictionary<UInt16, BaseStatus>();
//get all types of cucrent assembly
var allAssemblyTypes = Assembly.GetCallingAssembly().GetTypes();
//get all types from base status
var baseStatusTypes = allAssemblyTypes.Where(x => x.BaseType == typeof(BaseStatus));
//Place all concrete types in the foundtypes
List<Type> foundTypes = new List<Type>();
foreach (Type item in baseStatusTypes)
{
var temp = allAssemblyTypes.Where(x => x.BaseType == item)
.Select(x => new
{
StatusType = x,
UnitId = x.GetProperty("UnitId", BindingFlags.Public),
StatVal = x.GetProperty("StatusValue", BindingFlags.Public),
}
);
}
Temp contains now the correct type.
Problem is that if temp is type of StatStErrDefinition the StatusValue and UnitId Property is null.
The fact is that these members are instance members. Is there a way to get the values out of them?
First thing first : your LINQ query is pretty long.
divide it in different step and store them in different variables (or make properties out of them, whatever you prefer)
This is
easy to read / maintain
easy to debug
With this given I think you are able to solve your problem :)
To check if the class is of a certain type you could use the method .OfType
Use this method to get the value. Notice that you must make an instance in your case because the value change in your constructor.
public static object GetPropValue(Type src, string propName)
{
var prop = src.GetProperty(propName);
var instance = Activator.CreateInstance(src);
var value = prop.GetValue(instance);
return value;
}
Instead of
UnitId = x.GetProperty("UnitId", BindingFlags.Public),
use
UnitId = GetPropValue(x,"UnitId"),

IRegistrationConvention structuremap CtorDependency

I need to send a custom instance in Ctor in scanner. Hear is my code:
public class RunnableScanner : IRegistrationConvention
{
private readonly List<Module> modules;
public RunnableScanner()
{
var config = RegisterModulesConfig.GetConfig();
modules = config.Modules.ToList();
}
public void Process(Type type, Registry registry)
{
if (!type.IsAbstract && typeof(IRunnable).IsAssignableFrom(type))
{
var module = modules.SingleOrDefault(c => c.Name == type.Name);
if (module != null)
{
registry.For(typeof (IRunnable)).Use(type).Named(type.Name).CtorDependency<Scheduler>("scheduler")
.IsNamedInstance(module.Scheduler + "Scheduler");
}
}
}
}
the line :
registry.For(typeof (IRunnable)).Use(type).Named(type.Name).CtorDependency<Scheduler>("scheduler") .IsNamedInstance(module.Scheduler + "Scheduler");
i need to set a value for Scheduler ("Interval" property).
I did try this:
var sch = ObjectFactory.GetNamedInstance<Scheduler>(module.Scheduler + "Scheduler");
sch.Interval = module.Interval;
registry.For(typeof (IRunnable)).Use(type).Named(type.Name).CtorDependency<Scheduler>("scheduler").Is(sch);
but this don't work because ObjectFactory i cant use in Registry.
Sorry for my English.
I'm not a structuremap expert so maybe this is not the best/most elegant/effective solution.
But the following registration using a LambdaInstance seems to work
registry.For(typeof (IRunnable)).Use(type).Named(type.Name)
.CtorDependency<Scheduler>("scheduler")
.Is(new LambdaInstance<Scheduler>(c =>
{
var sch = c.GetInstance<Scheduler>(module.Scheduler + "Scheduler");
sch.Interval = module.Interval;
return sch;
})
);

Categories