How to have multiple GetExportedValues

Mar 12, 2010 at 10:59 AM

Hi,

I have a requirement where I would like to export based on two interfaces. For eg:

I have two interfaces : IWidget,, IPatientContext. There are usercontrol who decorates with

     [Export(typeof(IWidget))]
    [Export(typeof(IPatientContext))]

 

I would like to export only those user controls which have exported IWidget as well as IPatientContext.

Is there any way to container.GetExportedValues<IPatientContext,IWidget,IWidget>?

Thanks

Deepak

Mar 12, 2010 at 1:17 PM

Couldnt you just make a new interface that inherits from both? 

IWidgetAndPatientContext : IWidget, IPatientContext

And use that as the export?

Mar 13, 2010 at 11:08 AM

Thanks for the quick response.

It's a simple & sweet solution, but I will end up creating more interfaces, bcoz I just referred to only IWidget & IPatientContext. But there are user controls who participate in IWorksheetContext, IEpisodeContext etc.

My requirement is based on selected context whether IPatientContext, IWorksheetContext or IEpisodeContext I need to load the user controls which would be implementing IWidget along with these interfaces. In you suggestion I will be endup creating another 3 interfaces & so on if my contexts are getting increased.

Isn't MEF contains the easier way to get type of multiple exports?

 

Thanks

Deepak

Developer
Mar 14, 2010 at 12:07 AM

No MEF doesn't have any support for doing what you want. I think CodeFuzion's suggestion would be the easiest way for you to accomplish your goals. Even if you create some sort of API to accomplish what you want on the container, it won't be supported for people who wish to import in this way (importing is generally the recommended approach as opposed to pulling on the container directly, because it allows for static analysis of your dependencies).

Out of curiosity why is it that you want to import something that implements two different interfaces? Do you actually plan on using it under both interfaces? One thing you could also do is import all IWidgets then do a simple linq query to get only the ones that implement IPatientContext, or you could use some sort of metadata to indicate that this IWidget is a "patient" and use that to filter.

Mar 15, 2010 at 4:44 AM

I have a requirement where all the usercontrols implementing IWidget can be integrated inside my home page again there could be different context, so there could be say 10 widgets matching this criteria. I have another filter to load only those widgets which participates in particular context say IPatientContext. Now out of 10 widgets only 3 are implementing IPatientContext, that is the reason I wanted double filter.

Currently I am doing in that way only to get all the IWidgets type widgets then iterate through respective Contexts. But the problem is while iterating it is trying to instantiate the user control which is a performance hit for me as I don't want all the 10 widgets to be instantiated.

Here is my sample code snippet:

 

[ImportMany(typeof(IWidget))]
        public List<Lazy<IWidget>> Widgets
        {
            get;
            set;
        }

 

var patientWidgets = Widgets.GetEnumerator();
            

            //Iterates through the enumeration & add the patient widgets in panel
            while (patientWidgets.MoveNext())
            {
                IWidget patientWidget = patientWidgets.Current.Value;
                if (patientWidget is IPatientContext)

When I try to use patientWidgets.Current.Value, it is trying to instantiate each & every user control.

Could you please guide me how could I avoid this performance hit?

 

Thanks in advance.

Developer
Mar 15, 2010 at 6:48 PM

In MEF the way to accomplish what you want is to use metadata.

Example:
[Export(typeof(IWidget))]
[ExportMetadata("Type", "Patient")]
public class PatientWidget { }

public interface IWidgetMetadata { string Type { get; } }

[ImportMany]
public Lazy<IWidget, IWidgetMetadata> Widgets { get; set; } 

...
foreach (var widget in Widgets) {
  if (widget.Metadata.Type == "Patient")  {
    // use widget.Value  
  }

This will then allow you do use the metadata to filter without causing all the widgets to be constructed.

Mar 15, 2010 at 7:10 PM

Deepak, you can also create a custom export attribue which is strongly typed and more discoverable for the exporter. I cover that approach in this post in the section on Custom Exports.

Thanks
Glenn

Mar 22, 2010 at 5:16 AM

Hi,

Thanks for your reply, I tried Metadata approach it worked like a charm. Thank you sooooo much for quick response.

Thanks heaps!

Deepak

Mar 23, 2010 at 12:09 PM

Hi,

I am stuck with one more problem. Some of the usercontrols can have more than one contexts. So I tried following code, but unfortunately it is just taking the first export not the second one.

Exports code:

[ExportMetadata("Type", "IEpisodeContext", IsMultiple = true)]
[ExportMetadata("Type", "IPatientContext", IsMultiple = true)]

public partial class PatientDetailsControl : UserControl, IWidget, IPatientContext
  

Import code:

[ImportMany]
        public List<Lazy<IWidget, IWidgetMetataData>> Widgets { get; set; }

foreach (var wid in Widgets)
            {
               
                if (wid.Metadata.Type == "IPatientContext")
                {

 

When i check wid.Metadata.Type it just find IEpisodeContext not IPatientContext.

Any idea how to retrieve all the metadata types & compare?

Thanks in advance.

Developer
Mar 23, 2010 at 5:34 PM

The issue you are having here is that now your metadata doesn't match your metadata view. As soon as you make a metadata value allow multiple then the metadata view interface needs to support that. Try changing your metadata view interface to:

public interface IWidgetMetadata { IEnumerable<string> Type { get; } }

Mar 24, 2010 at 4:26 AM

oops I realized it now. I am sorry I didn't understand the actual purpose of metadata, I changed it as u suggested & it worked like a charm.

I really appreciate your quick help.

 

Thanks

Deepak