Catalog caching - What types are supported?

Jan 13, 2009 at 9:24 PM
Hi, I'm getting an exception when attempting to cache a catalog.

Here's a snippet of the code on which the exception is generated:

                    // Discover metadata for non-cached addin package
                    DirectoryPartCatalog newCatalog = new DirectoryPartCatalog(addinDirectory, false);

                    // save cache
                    ComposablePartCatalogCachingServices.CacheCatalog(newCatalog, metadataCachePath);

On the line where I attempt to save the cache, I'm getting an InvalidOperationException generated, the text of which is:

CM.Caching:UnsupportedCachedValue : Error : Objects of type 'System.Version' cannot be written into the cache.

It's a fairly self explanatory error.  It appears that the system does not support caching of metadata which has the type System.Version.
Stepping into the code, it appears that the issue is the method internal static CompositionResult LoadValue(this ILGenerator ilGenerator, object value)
in GenerationServices.cs

My question then is, what are the expected limitations of caching? If caching is only going to be supported for a subset of types, this would appear to a pretty big hole... Considering the system is likely to be used for open-ended extensibility, it's difficult to prescribe that only a subset of types are used for metadata.

Can anyone provide any advice/guidance on this?

I guess I could attempt to hack the code, to support Version in this narrow case but this wouldn't seem very feasible if I end up having to do this on a case by case basis.

How about allowing specification of type converters (or some variation) for types? Then use specified type converters to convert to types for which IL generation is supported.

Any advice greatly appreciated.

Thanks,
Phil

Jan 14, 2009 at 2:53 PM
Hmmm, a little more background on the use case.  I can see how it may be expected that the types used for metadata would be fairly limited.

Custom metadata attributes are limited to:
"An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type"

What is causing the error however, is not the metadata itself but the metadata view type I am using.  This interface provides information in a different form from the input arguments for the custom metadata class.

Here's a snippet of the attribute code, along with the metadata view type (the interface):

    /// <summary>
    /// This is the strongly typed view interface for the metadata attribute AddInMetadata which will be applied to our addin exports.
    /// </summary>
    public interface IAddInMetadata
    {
        /// <summary>
        /// Represents the version of the AddIn.  Used to differentiate between multiple
        /// concurrent installations of different versions of the same add-in.
        /// </summary>
        Version AddInVersion { get; }
        /// <summary>
        /// Identifies the publisher of the AddIn (e.g. 'Sensormatic')
        /// </summary>
        string PublisherName { get; }
    }

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field,
        AllowMultiple = false, Inherited = false)]
    [MetadataAttribute]
    public sealed class AddInMetadataAttribute : Attribute, IAddInMetadata
    {
        public AddInMetadataAttribute(Type addinType)
        {
            AddInVersion = addinType.Assembly.GetName().Version;
            PublisherName = GetPublisherName(addinType);
        }

        #region IAddInMetadata Members
        // properties all set by the attribute's constructor
        public Version AddInVersion { get; private set; }
        public string PublisherName { get; private set; }
        #endregion

        #region private static methods
        /// <summary>
        /// Calcs a publisher name assuming that the publisher name is the first part of the 
        /// namespace for the type
        /// </summary>
        /// <param name="addinType"></param>
        /// <returns></returns>
        private static string GetPublisherName(Type addinType)
        {
            string[] nameParts = addinType.Namespace.Split('.');
            return nameParts[0];
        }
        #endregion
    }

So, what I'm guessing is the issue is that MEF when caching metadata attempts to cache all the public properties exported by the metadata view type (in this case IAddInMetadata).  There are however in this case no compile time limitations on the types exposed by the interface.  The interface therefore has no limitations on the types supported - unlike the input arguments for the metadata constructor.

I'm going to work around this by changing my interface (and metadata attribute) so that the interface only exposes data in the exact same form (incl. types) as the metadata's input arguments.  I'll add extension methods which take this interface and expose the data in the desired form.

I have a suggestion here though: if this is to be a limitation in MEF - that the properties on metadata interfaces must only use types which are supported as input args to attributes, then MEF should perform a validation check at runtime that exported metadata or metadata interfaces only use the allowed types.

In other words, it shouldn't be the case that it only fails once you attempt to cache metadata.  It should fail whether you attempt to cache metadata or not.

Regards,
Phil

Jan 16, 2009 at 8:37 PM
Edited Jan 16, 2009 at 8:49 PM
Phil,

Thanks for the feedback. You are right, we only support a limited set of types for the metadata. In the future this will likely remain and be limited to the same set that is available in attributes today. We've talked about this internally, and we're thinking about tightening it to always error rather than just silently allowing it up until you attempt to cache the catalog.

Regards

David