Custom Application Block in Entlib 5.0

Topics: Building and extending application blocks
Nov 17, 2010 at 9:30 PM
Edited Nov 18, 2010 at 1:38 PM

Hello,

 I am trying to create a custom application block on EntLib 5.0 and keep getting the error below:

 Resolution of the dependency failed, type = "NameTypeConfigSample.PolicyProvider", name = "(none)".  Exception occurred while: while resolving.  Exception is: InvalidOperationException - The type PolicyProvider cannot be constructed. You must configure the container to supply this value.

 It almost seems that the config section is not even loaded from the config source.  I would really appreciate it if a walkthrough can be provided for creating a custom application block configuration.

Code:

  /// <summary>
  /// Returns the default IPolicyProvider instance.
  /// Guaranteed to return an intialized IPolicyProvider if no exception thrown
  /// </summary>
  /// <returns>Default Policy provider instance.</returns>
  /// <exception cref="ConfigurationException">Unable to create default IPolicyProvider</exception>
  public static IPolicyProvider GetPolicyProvider()
  {
   try
   {
    PolicyProviderFactory factory = new PolicyProviderFactory(ConfigurationSourceFactory.Create());
    return factory.CreateDefault();
   }
   catch (ActivationException configurationException)
   {
    TryLogConfigurationError(configurationException, "default");

    throw;
   }

 

App.config file:

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="passwordAgingConfiguration" type="NameTypeConfigSample.Configuration.PasswordAgingSettings, NameTypeConfigSample" />
  </configSections>

  <passwordAgingConfiguration defaultPolicyInstance="provider1">
    <policyProviders>
      <add
    name="provider1"
    type="NameTypeConfigSample.UserPolicyProvider, NameTypeConfigSample"
        id="1"
    />
    </policyProviders>
  </passwordAgingConfiguration>
</configuration>

 


Nov 17, 2010 at 10:18 PM

Do I need a typeRegistration configured?

Nov 18, 2010 at 1:05 AM

Based on the info you posted, it's not that easy to tell what's causing the problem but yes, you need to implement the ITypeRegistrationsProvider.   If you look at EntLib source code, this is usually implemented by the overall configuration settings of the application block (e.g. CacheManagerSettings, CryptographySettings) 

Unfortunately, there's no documentation on this topic yet.  See Chris Tavares' answer related to this.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 18, 2010 at 1:13 AM

I will be writing a hands-on lab on building a custom block, but it probably won't be done before the end of this year (which really isn't all that far off :-) ).

Yes, you need to implement the ITypeRegistrationsProvider interface, and you'll also need to plug your new block in explicitly in the configuration file (this is a new requirement in Entlib 5.)

 

Nov 18, 2010 at 1:43 PM
Edited Nov 18, 2010 at 2:28 PM
Unfortunately I cannot wait till EOY to get started.  Is there "draft" version of the lab you can make available.  Find below my PasswordAgingSettings class that implements ITypeRegistrationsProvider.  
When you say I have to explicitly plug my new block into the config, is it something more than adding it to the configSections?  I have included my App.Config file in my first post.
Code:
namespace NameTypeConfigSample.Configuration
{
   public class PasswordAgingSettings : SerializableConfigurationSection, ITypeRegistrationsProvider
    {
    /// 
		/// The name of the configuration section for the security providers.
		/// 
		public const string SectionName = "passwordAgingConfiguration";

        private const string policyProvidersProperty = "policyProviders";

		private const string defaultPolicyProviderNameProperty = "defaultPolicyInstance";

        /// 
        /// Initializes a new instance of the 
        ///  class.
        /// 
        public PasswordAgingSettings()
        {
        }

        /// 
        /// Gets the  section in the configuration source.
        /// 
        /// <param name="configurationSource" />The  to get the section from.
        /// The exception handling section.
        public static PasswordAgingSettings GetPasswordAgingSettings(IConfigurationSource configurationSource)
        {
            if (configurationSource == null) throw new ArgumentNullException("configurationSource");
            return (PasswordAgingSettings)configurationSource.GetSection(SectionName);
        }

        /// 
        /// The instance name of the default  instance.
        /// 
		[ConfigurationProperty(defaultPolicyProviderNameProperty, IsRequired= false)]
		public string DefaultPolicyProviderName
		{
			get
			{
                return (string)this[defaultPolicyProviderNameProperty];
			}
			set
			{
                this[defaultPolicyProviderNameProperty] = value;
			}
		}

        /// 
		/// Gets the collection of .
        /// 
        /// 
        /// The authorization providers available in configuration. The default is an empty collection.
        /// 
        /// 
        /// This property maps to the authorizationProviders element in configuration.
        /// 
        [ConfigurationProperty(policyProvidersProperty, IsRequired = false)]
        [ConfigurationCollection(typeof(PolicyProviderData))]
        public NameTypeConfigurationElementCollection PolicyProviders
		{
			get
			{
                return (NameTypeConfigurationElementCollection)base[policyProvidersProperty];
			}
		}

        
        /// 
        /// 
        /// 
        /// 
        public IEnumerable GetRegistrations(IConfigurationSource configurationSource)
        {
            //var defaultLoggerRegistrations = GetDefaultSecurityEventLoggerRegistrations(configurationSource);

            var policyProviderRegistrations = PolicyProviders.SelectMany(polp => polp.GetRegistrations(configurationSource));
            policyProviderRegistrations = SetDefaultPolicyProvider(policyProviderRegistrations);


            return policyProviderRegistrations;
        }

        /// 
        /// Return the  objects needed to reconfigure
        /// the container after a configuration source has changed.
        /// 
        /// If there are no reregistrations, return an empty sequence.
        /// <param name="configurationSource" />The  containing
        /// the configuration information.
        /// The sequence of  objects.
        public IEnumerable GetUpdatedRegistrations(IConfigurationSource configurationSource)
        {
            return GetRegistrations(configurationSource);
        }

        private TypeRegistration MarkAsPublicName(TypeRegistration registration)
        {
            if(registration.ServiceType == typeof(TService))
            {
                registration.IsPublicName = true;
            }
            return registration;
        }

        private IEnumerable SetDefaultPolicyProvider(IEnumerable policyProviderRegistrations)
        {
            foreach (TypeRegistration registration in policyProviderRegistrations)
            {
                if (registration.ServiceType == typeof(IPolicyProvider) && string.Equals(registration.Name, DefaultPolicyProviderName))
                {
                    registration.IsDefault = true;
                    yield return registration;
                }
                else
                {
                    yield return registration;
                }
            }
        }
    }
}
Nov 18, 2010 at 7:38 PM

Is what I have in the config file below enough or do I need something additional?

App.config file:

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="passwordAgingConfiguration" type="NameTypeConfigSample.Configuration.PasswordAgingSettings, NameTypeConfigSample" />
  </configSections>

  <passwordAgingConfiguration defaultPolicyInstance="provider1">
    <policyProviders>
      <add
    name="provider1"
    type="NameTypeConfigSample.UserPolicyProvider, NameTypeConfigSample"
        id="1"
    />
    </policyProviders>
  </passwordAgingConfiguration>
</configuration>

Nov 18, 2010 at 8:37 PM

You'll need to add another section telling Entlib to look at your custom section as well. that looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="passwordAgingConfiguration" type="NameTypeConfigSample.Configuration.PasswordAgingSettings, NameTypeConfigSample" />
    <section name="typeRegistrationProvidersConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.TypeRegistrationProvidersConfigurationSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  </configSections>

  <passwordAgingConfiguration defaultPolicyInstance="provider1">
    <policyProviders>
      <add
    name="provider1"
    type="NameTypeConfigSample.UserPolicyProvider, NameTypeConfigSample"
        id="1"
    />
    </policyProviders>
  </passwordAgingConfiguration>

  <typeRegistrationProvidersConfiguration>
    <add name="Password Aging Block" sectionName="passwordAgingConfiguration"/>
  </typeRegistrationProvidersConfiguration>

</configuration>

Notice the introduction of the typeRegistrationProvidersConfiguration section, pointing at your new configuration section.

 

Nov 18, 2010 at 9:41 PM

Awesome thank you for that!!!

So now, I can see the type registration wiring up and loading my UserPolicyProviderData object from config, but when I do a

PolicyProviderFactory factory = new PolicyProviderFactory(ConfigurationSourceFactory.Create());

 

return factory.CreateDefault();

I get the exception

Resolution of the dependency failed, type = "NameTypeConfigSample.PolicyProvider", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type PolicyProvider cannot be constructed. You must configure the container to supply this value.
-----------------------------------------------
At the time of the exception, the container was:

  Resolving NameTypeConfigSample.PolicyProvider,(none)

at:

public T CreateDefault()

{

return container.GetInstance<T>();

}

Almost seems like the UnityContainer does not have the definition of the class in it to instantiate an object.  Help???

Nov 19, 2010 at 3:15 AM

How are you returning an instance of IPolicyProvider inside the factory.CreateDefault method? In addition, you can debug through your GetRegistrations method and see if the policyProviderRegistrations object it is returning contains the corresponding TypeRegistration item set to as the default for IPolicyProvider.

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 19, 2010 at 7:09 PM

You can also check the Unity container's .Registrations collection to see if the type is actually registered the way you think it is.

 

Nov 30, 2010 at 9:22 PM
Edited Nov 30, 2010 at 10:45 PM

@ctavares, the container.Registrations has:

this.Registrations.ToList()[3]
{Microsoft.Practices.Unity.ContainerRegistration}
    buildKey: {Build Key[NameTypeConfigSample.IPolicyProvider, provider1]}
    LifetimeManager: {Microsoft.Practices.Unity.TransientLifetimeManager}
    LifetimeManagerType: {Name = "TransientLifetimeManager" FullName = "Microsoft.Practices.Unity.TransientLifetimeManager"}
    MappedToType: {Name = "UserPolicyProvider" FullName = "NameTypeConfigSample.UserPolicyProvider"}
    Name: "provider1"
    RegisteredType: {Name = "IPolicyProvider" FullName = "NameTypeConfigSample.IPolicyProvider"}
I figured out what I was doing wrong.  I had PolicyProviderFactory : ContainerBasedInstanceFactory<PolicyProvider>, 
when I should have had PolicyProviderFactory : ContainerBasedInstanceFactory<IPolicyProvider>
What do I need to do to be able to manage this app block in the Configuration Manager application?
Jan 6, 2011 at 9:54 PM
ctavares wrote:

I will be writing a hands-on lab on building a custom block, but it probably won't be done before the end of this year (which really isn't all that far off :-) ).

Yes, you need to implement the ITypeRegistrationsProvider interface, and you'll also need to plug your new block in explicitly in the configuration file (this is a new requirement in Entlib 5.)

 

 Hey ctavares,

Was the hands-on lab on building a custom block published?  Let me know.

Jan 6, 2011 at 10:24 PM

It hasn't been published because it's not done yet. I'm still working on the first draft (this week actually).

I had the code several months ago, but writing the text in a sufficiently clear manner is proving challenging.

 

Jan 26, 2011 at 9:10 AM

In this context I am curious, how I can tell the configuration tool to write out my custim application block AND the typemapping-section.

There the  "protected virtual ConfigurationSection CreateConfigurationSection()" that returns only ONE configuration section, but I have to specify two (the typeRegistrationProvidersConfiguration and my configurationobject)

What is the recommended way?



       

Jan 26, 2011 at 11:00 PM

The config tool doesn't do that right now - I thought we'd put it in there (as a separate "block" ) but I don't see it now.

However, the tool won't touch any sections it doesn't know about, so you'll need to add the type registration provider section manually, and the tool won't interfere with it otherwise. Not very good, I admit, but it's the best we have at the moment.

 

Feb 23, 2011 at 7:11 PM

Any ETA on the hands-on lab for custom clocks  / EL5 ?

Feb 23, 2011 at 7:17 PM

Thats custom blocks :) sorry

Feb 24, 2011 at 7:05 AM

We're not sure, we'll ask this with the EntLib team.

 

Sarah Urmeneta
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Feb 24, 2011 at 9:03 AM

 

The custom block lab should be making its way through the MSDN publishing process right now.

Feb 26, 2011 at 1:16 AM

The custom block lab (#4) is now available from the download center.

Grigori

Apr 1, 2011 at 9:46 PM

I'm running into exactly the same issue and I've based my work on the tutorial above.

I've written code like this:

            var locator = EnterpriseLibraryContainer.CreateDefaultContainer();
            var defaultValue = locator.GetInstance<MyBaseType>();

Through debugging I confirm that I have a locator that appears virtually identically to what Sigdam posted earlier.  locator has a Registrations member that contains an entry with these basic properties:

RegisteredType: MyBaseType

MappedToType: MyDerivedType

Name: Type.__default__

Yet when I step into the Unity method that creates the instance, by the time it gets into the PreBuildUp method in the TypeInterceptionStrategy class, the context has a build key that only references the base type.  It looks like Unity is basically ignoring my type registration.

This is the most conviluted framework I've seen in my career.  I want to rip my hair out.  Every single simple thing has been a multi-hour nightmare.

Apr 1, 2011 at 9:51 PM

Update: it works if and only if I specify the automatically generated name of "BaseType.__default__" which I had to scour through the debugger to find.

Why does this not work when A) I specify no parameters or B) I specify the friendly name specified in my app.config?

My app.config basically looks like this:

    <myCustomBlockSection defaultBlockObject="My Friendly Name">
        <myBlockObjects>
            <add name="My Friendly Name" />

I expect to be able to call locator.GetInstance<MyBaseType>() or locator.GetInstance<MyBaseType>("My Friendly Name").  What is the story here?  Am I going to have to fix this debacle myself and build my own copy of Unity with my application?

Apr 4, 2011 at 4:24 AM

Evan, sounds like you didn’t set IsDefaultRegistration on the type registration for the default object in your block.

This step is covered in the lab.

Apr 5, 2011 at 5:04 AM

/facepalm

Thanks.  I knew I was overlooking something because the Greeter sample works exactly like it is supposed to... I just couldn't track it down.

Page 64, black and white.  That's what I get for Friday afternoon.

You saved me a ton of time.

Evan