I’m having some trouble with LinFu 2.3’s IoC and I think I’ve found a bug.
I’ve created the following test service:
public interface IService
{
int Int { get; }
string Text { get; }
bool Bool { get; }
}
[Implements(typeof(IService<>), LifecycleType.OncePerRequest)]
public class S : IService
{
public S(int i, string text, bool b)
{
Int = i;
Text = text;
Bool = b;
}
public int Int { get; set; }
public string Text { get; set; }
public bool Bool { get; set; }
}
When I try to instantiate the service by providing the constructor arguments to GetService it fails with a ServiceNotFound exception.
ServiceContainer container = new ServiceContainer();
container.Initialize();
container.LoadFrom(typeof(UnitTest1).Assembly);
var s = container.GetService<IService>(42, "frobozz", false);
If I strip off the generics, changing IService to IService and S to S it works fine. Debugging LinFu it looks like the problem is in FactoryStorage.GetFactory:
public override IFactory GetFactory(IServiceInfo serviceInfo)
{
// Attempt to create the service type using
// the strongly-typed arguments
var factory = base.GetFactory(serviceInfo);
var serviceType = serviceInfo.ServiceType;
var serviceName = serviceInfo.ServiceName;
// Use the default factory for this service type if no other factory exists
var defaultServiceInfo = new ServiceInfo(serviceInfo.ServiceName, serviceInfo.ServiceType);
if (factory == null && base.ContainsFactory(defaultServiceInfo))
factory = base.GetFactory(defaultServiceInfo);
// Attempt to create the service type using
// the generic factories, if possible
if (factory != null || !serviceType.IsGenericType)
return factory;
var definitionType = serviceType.GetGenericTypeDefinition();
var genericServiceInfo = new ServiceInfo(serviceName, definitionType, serviceInfo.ArgumentTypes);
if (base.ContainsFactory(genericServiceInfo))
return base.GetFactory(genericServiceInfo);
With the (working) non-generic case, the _entries dictionary contains a key for IService with 0 arguments. The construction of defaultServiceInfo with no arguments matches that, and the factory matching defaultServiceInfo is returned and all is well.
With the (not working) generic case the _entries dictionary contains a key for IService<> with 0 arguments. The construction of defaultServiceInfo strips the arguments from my request but does not strip the int from my provided type of IService. Thus defaultServiceInfo does not return a factory (as IService<> != IService). The code falls through to the next step which strips the generic parameter out but does not strip the arguments out, so it ends up looking for the service type IService<> but with whatever arguments I provide. This doesn’t match either and the code falls all the way through w/o finding the default factory.
I’m not sure whether the correct fix is to change defaultServiceInfo so it strips the generic arguments out as well as the constructor argumennts, or whether an addition step of looking for something with generic arguments & constructor arguments stripped should be added.
As far as I can tell this makes the functionality totally broken as there is no workaround.
I made the following change to GetFactory() to test my hypothesis and with the change the code works as expected (whether the change breaks other things I do not know):
///
/// Obtains the instance that can instantiate the
/// service described by the object instance.
///
/// The object that describes the service to be created.
/// A instance if the service can be instantiated; otherwise, it will return false.
public override IFactory GetFactory(IServiceInfo serviceInfo)
{
// Attempt to create the service type using
// the strongly-typed arguments
var factory = base.GetFactory(serviceInfo);
var serviceType = serviceInfo.ServiceType;
var serviceName = serviceInfo.ServiceName;
// Use the default factory for this service type if no other factory exists
var defaultServiceInfo = new ServiceInfo(serviceInfo.ServiceName, serviceInfo.ServiceType);
if (factory == null && base.ContainsFactory(defaultServiceInfo))
factory = base.GetFactory(defaultServiceInfo);
// Attempt to create the service type using
// the generic factories, if possible
if (factory != null || !serviceType.IsGenericType)
return factory;
/*********************************************************************************************/
// Begin MJS Hack.
// We've got a generic service request, repeat the defaultServiceInfo query above but this time
// stripping the generic arguments as well as the constructor arguments.
defaultServiceInfo = new ServiceInfo(serviceName, serviceType.GetGenericTypeDefinition());
if (factory == null && base.ContainsFactory(defaultServiceInfo))
{
factory = base.GetFactory(defaultServiceInfo);
if (null != factory) return factory;
}
// End MJS Hack.
/*********************************************************************************************/
var definitionType = serviceType.GetGenericTypeDefinition();
var genericServiceInfo = new ServiceInfo(serviceName, definitionType, serviceInfo.ArgumentTypes);
if (base.ContainsFactory(genericServiceInfo))
return base.GetFactory(genericServiceInfo);
I’m having some trouble with LinFu 2.3’s IoC and I think I’ve found a bug.
I’ve created the following test service:
public interface IService
{
int Int { get; }
string Text { get; }
bool Bool { get; }
}
[Implements(typeof(IService<>), LifecycleType.OncePerRequest)] public class S : IService
{
public S(int i, string text, bool b)
{
Int = i;
Text = text;
Bool = b;
}
}
When I try to instantiate the service by providing the constructor arguments to GetService it fails with a ServiceNotFound exception.
ServiceContainer container = new ServiceContainer(); container.Initialize(); container.LoadFrom(typeof(UnitTest1).Assembly); var s = container.GetService<IService>(42, "frobozz", false);
If I strip off the generics, changing IService to IService and S to S it works fine. Debugging LinFu it looks like the problem is in FactoryStorage.GetFactory:
public override IFactory GetFactory(IServiceInfo serviceInfo) { // Attempt to create the service type using // the strongly-typed arguments var factory = base.GetFactory(serviceInfo); var serviceType = serviceInfo.ServiceType; var serviceName = serviceInfo.ServiceName;
With the (working) non-generic case, the _entries dictionary contains a key for IService with 0 arguments. The construction of defaultServiceInfo with no arguments matches that, and the factory matching defaultServiceInfo is returned and all is well.
With the (not working) generic case the _entries dictionary contains a key for IService<> with 0 arguments. The construction of defaultServiceInfo strips the arguments from my request but does not strip the int from my provided type of IService. Thus defaultServiceInfo does not return a factory (as IService<> != IService). The code falls through to the next step which strips the generic parameter out but does not strip the arguments out, so it ends up looking for the service type IService<> but with whatever arguments I provide. This doesn’t match either and the code falls all the way through w/o finding the default factory.
I’m not sure whether the correct fix is to change defaultServiceInfo so it strips the generic arguments out as well as the constructor argumennts, or whether an addition step of looking for something with generic arguments & constructor arguments stripped should be added.
As far as I can tell this makes the functionality totally broken as there is no workaround.
I made the following change to GetFactory() to test my hypothesis and with the change the code works as expected (whether the change breaks other things I do not know):
///
/// Obtains the instance that can instantiate the
/// service described by the object instance.
///
/// The object that describes the service to be created.
/// A instance if the service can be instantiated; otherwise, it will return false .
public override IFactory GetFactory(IServiceInfo serviceInfo)
{
// Attempt to create the service type using
// the strongly-typed arguments
var factory = base.GetFactory(serviceInfo);
var serviceType = serviceInfo.ServiceType;
var serviceName = serviceInfo.ServiceName;