Problems with internal constructors in StructureMap

140 views
Skip to first unread message

Christian Lundestad

unread,
Jul 10, 2009, 8:55:14 AM7/10/09
to structuremap-users
Hi,

I have just started using StructureMap 2.5.3, and I have to say it's a
great tool. I especially like the Registry DSL and all its powerful
features.

I am currently working on fitting StructureMap into a quite large web
application and I'm running into some problems when working with
internal classes and constructors. The documentation mentions that
this should be possible using the [assembly:InternalsVisibleTo]
attribute. but I have been unable to make this work with automatic
type registration or the normal configuration syntax.

Let's say I have a service that depends on another service that I do
not want to expose as public. To be able to inject this internal
service into the constructor of the public service, this constructor
must be declared internal:

public class PublicService : IPublicService
{
private readonly IInternalService internalService;

internal PublicService(IInternalService internalService)
{
this.internalService = internalService;
}
}

Now, when configuring StructureMap the obvious way:

ForRequestedType<IPublicService>
().TheDefaultIsConcreteType<PublicService>();
ForRequestedType<IInternalService>
().TheDefaultIsConcreteType<InternalService>();

I get this exception on the first line above:

StructureMap.StructureMapException was unhandled
Message="StructureMap Exception Code: 180\nStructureMap cannot
construct objects of Class
StructureMapInternalConstructors.PublicService,
StructureMapInternalConstructors, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null because there is no public constructor found."
Source="StructureMap"
ErrorCode=180
StackTrace:
at
StructureMap.Configuration.DSL.ExpressionValidator.ValidateExpression.IntoPluginType
(Type pluginType)
at
StructureMap.Configuration.DSL.Expressions.CreatePluginFamilyExpression`1.TheDefaultIsConcreteType
[CONCRETETYPE]()
at StructureMapInternalConstructors.MyRegistry..ctor() in D:
\CVLDev\StructureMapInternalConstructors
\StructureMapInternalConstructors\MyRegistry.cs:line 9
at StructureMapInternalConstructors.Program.<Main>b__0
(IInitializationExpression x) in D:\CVLDev
\StructureMapInternalConstructors\StructureMapInternalConstructors
\Program.cs:line 10
at StructureMap.ObjectFactory.Initialize(Action`1 action)
at StructureMapInternalConstructors.Program.Main(String[] args)
in D:\CVLDev\StructureMapInternalConstructors
\StructureMapInternalConstructors\Program.cs:line 10
at System.AppDomain._nExecuteAssembly(Assembly assembly, String
[] args)
at
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:


While this seems to work:

ForRequestedType<IPublicService>().TheDefault.Is.ConstructedBy(context
=> new PublicService(context.GetInstance<IInternalService>()));
ForRequestedType<IInternalService>().TheDefault.Is.ConstructedBy(() =>
new InternalService());

I don't like the idea of either having to make all my classes and
interfaces public, or having to configure my entire application like
this. I would appreciate any input on whether I'm doing anything
wrong, or whether this is a bug that is likely to be fixed.

Regards,
Christian Lundestad

Christian Lundestad

unread,
Jul 16, 2009, 4:52:36 AM7/16/09
to structuremap-users
I downloaded the source and had a quick look at this myself.

The problem with internal constructors seems to limited to a few
issues with type rules and with making the internals of my assemblies
visible to StructureMap's dynamically emitted assemblies.

I was able to patch things up so they work for my simple test cases,
but I would appreciate Jeremy's opinion on whether this is something
that will be fixed in future versions of StructureMap.

Here is what I did:
Rewrote StructureMap.Graph.Constructor.GetGreediestConstructor() to
also return internal constructors:

public static ConstructorInfo GetGreediestConstructor(Type
pluggedType)
{
ConstructorInfo returnValue = null;

foreach (ConstructorInfo constructor in
pluggedType.GetConstructors(BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance))
{
if (constructor.IsPrivate && !constructor.IsAssembly)
continue;

if (returnValue == null)
{
returnValue = constructor;
}
else if (constructor.GetParameters().Length >
returnValue.GetParameters().Length)
{
returnValue = constructor;
}
}

return returnValue;
}

And the same for TypeRules.noPublicConstructors():

private static bool noPublicOrInternalConstructors(Type pluggedType)
{
var constructors = pluggedType.GetConstructors
(BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance);
return !constructors.Any(c => (c.IsPublic ||
c.IsAssembly));
}


The second issue is more tricky - making internals visible to the
dynamic instance builder assemblies.
As far as I can tell, the only way to do this is if I know the name(s)
of the dynamically emitted assemblies in advance, so that I can use
the [assembly:InternalsVisibleTo] attribute.
StructureMap.Emitting.InstanceBuilderAssembly uses a dynamically
generated GUID as part of the assembly name, so this is not possible
with the current implementation.
The dynamic assembly also needs to have the PublicKey and
PublicKeyToken properties set to valid values.

So by using a well known name for the dynamic assembly and setting the
PublicKey to StructureMap's public key I was able to make things work
for me, but of course I don't know how this will impact the rest of
the code base.

Again, I would appreciate Jeremy's thoughts on whether the possibility
of using internal constructors is something that will find its way
into future versions of StructureMap.

Regards,
Christian Lundestad





On Jul 10, 2:55 pm, Christian Lundestad <christ...@lundestad.com>
wrote:
> StructureMap.Configuration.DSL.ExpressionValidator.ValidateExpression.IntoP­luginType
> (Type pluginType)
>        at
> StructureMap.Configuration.DSL.Expressions.CreatePluginFamilyExpression`1.T­heDefaultIsConcreteType

Randy Regnier

unread,
Jul 24, 2009, 9:51:22 AM7/24/09
to structure...@googlegroups.com
On Thu, Jul 16, 2009 at 3:52 AM, Christian Lundestad <chri...@lundestad.com> wrote:

Again, I would appreciate Jeremy's thoughts on whether the possibility
of using internal constructors is something that will find its way
into future versions of StructureMap.

I would also be interested in knowing the answer to the question, as we are in a similar situation with needing to have some classes be internal. Thanks.

Randy Regnier
Reply all
Reply to author
Forward
0 new messages