Unexpected Exception Has Me Befuddled

Aug 31, 2009 at 3:47 PM

I’ve been befuddled by a problem for a good number of hours here.

I have this metadata contract:

 

public interface ILoopValuesComposition 
    {
        string LoopTypeNames { get;  }
    }

Applied though a custom attribute:

 

    [MetadataAttribute()]

    public class LoopValuesAttribute : ExportAttribute, ILoopValuesComposition

    {

        private string _typeNames;

 

        public LoopValuesAttribute(string types)

            : base(typeof(ILoopValues))

        {

            this._typeNames = types;

        }

 

        public LoopValuesAttribute(params Type[] types)

            : base(typeof(ILoopValues))

        {

            var s = string.Empty;

            foreach (var t in types)

            {

                s += t.FullName;

            }

            this._typeNames = s;

        }

 

        //public LoopValuesAttribute(Type type, string types)

        //    : base(type)

        //{ this.mTypeNames = types; }

 

        public string LoopTypeNames

        {

            get

            { return this._typeNames; }

        }

    }

 

The error I get is

 

Test method AppVenture.GenerationTests.When_running_simple_database_T4_templates.Can_find_loopValue_service threw exception:  System.ComponentModel.Composition.CompositionContractMismatchException: Unable to create an Instance of the Metadata view 'AppVenture.Common.ILoopValuesComposition' because the exporter exported the metadata  for the item 'LoopTypeNames' with the value 'System.String[]' as type 'System.String[]' but the view imports it as type 'System.String'. --->  System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. --->  System.InvalidCastException: Unable to cast object of type 'System.String[]' to type 'System.String'..

 

When I check the inner dictionary (thank you for source code!) it contains (Readonly dictionary TryGetValue):

? _innerDictionary.Keys

Count = 6

    [0]: "Group"

    [1]: "Id"

    [2]: "Priority"

    [3]: "LoopTypeNames"

    [4]: "ExportTypeIdentity"

    [5]: "System.ComponentModel.Composition.CreationPolicy"

? _innerDictionary.Values

Count = 6

    [0]: null

    [1]: null

    [2]: -1

    [3]: {string[1]}

    [4]: "AppVenture.Common.ILoopValues"

    [5]: NonShared

From the only export that’s found (GetExportProvider.GetExportOverrides.(        private IEnumerable<Lazy<T, TMetadataView>> GetExportsCore<T, TMetadataView>(string contractName))

 

? export

{System.ComponentModel.Composition.Hosting.CatalogExportProvider.NonSharedCatalogExport}

    [System.ComponentModel.Composition.Hosting.CatalogExportProvider.NonSharedCatalogExport]: {System.ComponentModel.Composition.Hosting.CatalogExportProvider.NonSharedCatalogExport}

    _definition: null

    _exportedValue: {object}

    _exportedValueGetter: null

    Definition: {AppVenture.DefaultMetadataServices.DbDatabaseMetadata (ContractName="AppVenture.Common.ILoopValues")}

    Metadata: Count = 6

    Value: {AppVenture.DefaultMetadataServices.DbDatabaseMetadata}

 

Where DBDatabaseMetadata is (I’ve also used the type array overload of my constructor and get the same result)

 

    [DbDatabaseMetadata(Priority = Utilities.DefaultPriority)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    [LoopValues("DbTableMetadata,IDbFunctionMetadata,IDbStoredProcMetadata,IDbUdtMetadata,IDbViewMetadata")]
    public partial class DbDatabaseMetadata
      : IDbDatabaseMetadata

How on earth is it finding a string array in this? Is there any code I neglected to include? Surely the answer is staring me in the face?

 

Help will be greatly appreciated.

Aug 31, 2009 at 4:32 PM
Edited Aug 31, 2009 at 6:19 PM

Hi Kathleen

You need to add [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] to your LoopValues attribute.

Check the bottom of this thread for an explanation of why: http://mef.codeplex.com/Thread/View.aspx?ThreadId=63152

Regards

Glenn

Developer
Aug 31, 2009 at 6:09 PM

Actually if you only expect there to be a single LoopValuesAttribute on an Export thet set [AttributeUsages(..., AllowMultiple=false)] (by default AllowMultiple=true because it is inherited from ExportAttribute). However if you expect multiple of these then you need to change your LoopTypeNames property in your metadata view to be of type string[].

 

Sep 2, 2009 at 12:51 PM

Thank you Glenn and Wes.

I want to add some information for anyone else encountering this.

The default for AllowMultiple on a new attribute is false. Therefore, this isn't needed in many cases. But, I was deriving from ExportAttribute for this custom attribute. For good reason the ExportAttribute has AllowMultple=true. Therefore, you need to force it back to fals in your deriving custom attribute.

Kathleen