Constructor Parameters

Feb 12, 2009 at 5:43 PM
The part I am creating has an intimate relationship with a piece of data. The part has no meaning without this particular value being defined. I'd like to set this value in the constructor, rather than assigning it afte the fact so I don't have an invalid object for some length of time.

I realize that in theory I could call another part to retrieve this value, but I greatly dislike that approach as it is a transitory value and that cursor based approach sounds like a nightmare, even before I go to mulit-threading.
Feb 12, 2009 at 5:53 PM
You can pass params through the constructor as imports. Is that what you are trying to do?

Our [ImportingConstructor] will allow you to use constructors.
Feb 12, 2009 at 6:29 PM
So, somewhere I have this (I'm sorry it copied so ugly, not sure the trick there)

 

<CompositionOptions(CreationPolicy:=CreationPolicy.Shared)> _
<Export(GetType(ITemplateWrapper))> _
<TemplateWrapperCompositionData(Extension:="t4")> _
Public Class TemplateWrapperT4

     Inherits TemplateWrapper

 

 

     Public Sub New(ByVal name As String)
         MyBase.new(name)
     End Sub

 

 

 

...

Somewhere else in a helper method I have the code:

 

    Dim exports = mContainer.GetExports(Of ITemplateWrapper, ITemplateWrapperCompositionRules)()
    ' Do stuff to select templateWrapperExport
    Dim
templateWrapper = templateWrapperExport.GetExportedObject()

I was under the impression from reading the docs that if I put "ImportingConstructor" on the constructor, it would look for any service supplying a string? Is it smarter than that? I want to take what feels a simpler approach of just passing parameters to the GetExportedObject method. In my fantasies, I'd like it to have a "sort-of shared" mode where it was shared if a hash of the constructor values matched and new if the parameters were new.

And the other problem is that this approach sets up a cursor like approach that supplies this name, and then changes the name to the next template. Assuming this is in a multi-threaded environment which I ceratinly hope to achieve. That approach seems really risky and I don't see it working in a multi-threaded environment (which I'm just trying to figure out anyway).

 

 

 

 

Feb 12, 2009 at 6:45 PM
Not sure if this answers your question, but I'll try. By default when you use importing contructor, it will look at each parameter, and use the type as the contract to find imports. You can add the [Import] attribute explicitly on the parameter if you want to provide a specific contract.

So you could do

namespace MyApp {
  public class OrderProcessor{
    [ImportingConstructor]
    public OrderProcess(ILogger logger) {
    }
  }
}

but you could also do

public class MessageSender {
  [ImportingConstructor]
  public MessageSender(
    [Import("Timeout"] int timeout,
    [Import("Retries"] int retries
  ) 
 {}
}
}
In the first case, the logger is imported by deriving the contract from the type which would be "MyApp.ILogger". In the second case, I have 2 int's so I need to explicitly provide the contract in order that the container can find it.

Does this help?
Feb 12, 2009 at 6:48 PM
In terms of a multi-threaded environment, you probably want to consider having separate containers for scoping and possibily having a parent/child relationship for sharing common, non thread-affinity based services.
Feb 12, 2009 at 9:47 PM
OK,this is not exact, but it gives the general idea...

Dim exports = mContainer.GetExports(Of ITemplateWrapper, ITemplateWrapperCompositionRules)()
For Each name in names
    ' Do stuff to select templateWrapperExport based on name
    Dim
templateWrapper = templateWrapperExport.GetExportedObject()
    templateWrapper.SetFileName(name)
    ' Do something
Next

In this particular case, the templateWrapper knows whether it should be shared. This code does not know so much assume it may not be shared.

I'm having trouble getting my head around this as a container problem, well, unless I explicitly add an export to the container. I'm not sure I see a benefit in that over other approaches.

Kathleen
Developer
Feb 13, 2009 at 4:34 PM

If you need to pass unique name instances to every TemplateWrapper then doing an ImportingConstructor will not help you. You would need some Set\Initialize method on your TemplateWrapper interface.

As far as passing parameters to GetExports/GetExportedObjects that would assume that their only purpose is to act as a Factory for your TemplateWrapper class, which is not their purpose at all. Their only purpose is to execute a query for exports in our system by asking a set of ExportProviders, and they are oblivious to how the objects are actually discovered and constructed. With our default attributed programming model exports are found by looking for Export attributes, and it is simply an implementation detail of that programming model that actually constructs the objects.

Another way to think about this is that GetExports/GetExportedObjects may end up causing no objects to be constructed (i.e. a shared object already exists) or possibly end up causing hundreds of objects to be constructed (i.e. an entire object graph). Therefore there is not a direct mapping from GetExports/GetExportedObjects to a single constructor to even allow us to pass parameters down.