Unity and DAAB

Oct 7, 2008 at 4:52 PM
Edited Oct 7, 2008 at 4:53 PM
Trying to use with a constructor, as in:
MyConsumer(MyDAL da)
{
 _myDAL = da;
}
MyDAL(Database da)
{
   _database = db;
}

WITHOUT Extensions added to the default container (EnterpriseLibraryCoreExtension and DataAccessBlockExtension), I get this error when I try to Resolve IMyConsumer:
The type Database does not have an accessible constructor.

WITH Extensions added, I get this error, at the section.Containers.Default.Configure call:
Unable to find the requested .Net Framework Data Provider.  It may not be installed.

below is the config section:
  <unity>
    <containers>
      <container>
        <types>
          <type type="SomeNamespace.IMyConsumer, SomeNamespace" mapTo="SomeNamespace.MyConsumer, SomeNamespace" />
        </types>
        <extensions>
          <add type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.EnterpriseLibraryCoreExtension, Microsoft.Practices.EnterpriseLibrary.Common" />
          <add type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.Unity.DataAccessBlockExtension, Microsoft.Practices.EnterpriseLibrary.Data" />
        </extensions>
      </container>
    </containers>
  </unity>

What am I doing wrong?

Oct 7, 2008 at 6:39 PM
I wrote a tutorial on this:

Enterprise Library 4.0 Data Access Application Block ( DAAB ) and Unity IoC Tutorial - DataAccessBlockExtension

as well as a screecast:

Enterprise Library 4.0 Data Access Application Block ( DAAB ) and Unity IoC Screencast

Hopefully one of those will give you the answer.

If you are using Unity and Enterprise Library, I can't think of any reason why you wouldn't want to use the Extensions. All they do is populate the UnityContainer with the necessary services so you don't have to :)

Regards,

Dave
Oct 7, 2008 at 6:52 PM
Edited Oct 7, 2008 at 6:53 PM
Thanks... I agree and have been using your tutorials, however, when added to our architecture, the error above is our result.

I would prefer to use the Extensions.... however the result is the error shown above.  In addition, I've pulled out a full stack trace of the error when it fails when extensions are enabled:

   at System.Data.Common.DbProviderFactories.GetFactory(String providerInvariantName)
   at Microsoft.Practices.EnterpriseLibrary.Data.DatabaseConfigurationView.GetDefaultMapping(String name, String dbProviderName)
   at Microsoft.Practices.EnterpriseLibrary.Data.DatabaseConfigurationView.GetProviderMapping(String name, String dbProviderName)
   at Microsoft.Practices.EnterpriseLibrary.Data.Configuration.Unity.DataAccessBlockExtension.Initialize()
   at Microsoft.Practices.Unity.UnityContainerExtension.InitializeExtension(ExtensionContext context)
   at Microsoft.Practices.Unity.UnityContainer.AddExtension(UnityContainerExtension extension)
   at Microsoft.Practices.Unity.Configuration.UnityContainerExtensionElement.Configure(IUnityContainer container)
   at Microsoft.Practices.Unity.Configuration.UnityContainerElement.Configure(IUnityContainer container)
   at Operitel.LearnFlex.BusinessService.UnityWCFIntegration.UnityServiceHostFactory.CreateServiceHost(Type serviceType, Uri[] baseAddresses) in D:\MyWork\BusinessService\BusinessService.UnityWCFIntegration\UnityServiceHostFactory.cs:line 25
   at System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses)
   at System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath)
   at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(String normalizedVirtualPath)
   at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)


The error message that it cannot find the .NET Framework Data Provider is not very helpful... with the various layers of Unity and DAAB, I'm not sure what it cannot find.  An assembly name of what it can't find would be an awesome addition to this error message!

Oct 7, 2008 at 8:07 PM
You really need to use the extensions - they do a lot of otherwise painful work for you.

My first suggestion would be to try to access the Database object directly. Does that work? It sounds to me like you may have an issue in your database settings or something, and using Unity is obscuring the underlying cause.

-Chris
Oct 7, 2008 at 9:30 PM
Thanks.  I will use the extensions, I just need to get things working.  If I avoid Unity for part of the app by doing this in the constructor:

        private Database _db;
        public DataAction()
        {
            _db = DatabaseFactory.CreateDatabase();
        }

Then the DAAB seems to work just fine.

But when I do this:
        private Database _db;
        public DataAction(Database db)
        {
            _db = db;
        }

In both cases, the Extensions are defined in the config file.... just in the first case, I guess they aren't used.

To clarify the objects used (I took a shortcut before, but maybe that is part of the problem)... the chain is 3 levels deep:

MyConsumer()  (internally calls Resolve<Adapter>())
Adapter(DataAction da)
DataAction(Database db)

Closer look reveals that the error comes from the attempt to resolve the parent object, which depends on DataAction.  That error is:

Resolution of the dependency failed, type = "Adapter", name = "".
Exception message is: The current build operation (build key Build Key[Adapter, null]) failed:
The parameter da could not be resolved when attempting to call constructor Adapter(DataAction da).
(Strategy type Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy, index 2)

Now if I set a breakpoint and check the InnerException (about 5 levels deep), it says this:

The type Database does not have an accessible constructor.

Does this mean that Unity cannot find one of the extensions?




Oct 8, 2008 at 12:18 AM

Based on your stack trace, I think the problem is not with Unity and the EntLib Extensions, but with the connection string you are using with the Data Access Application Block ( DAAB ).

The DAAB looks at the providerName in your connection string to determine which concrete database class to create for use with your database. For example, providerName="System.Data.SqlClient" will cause the DAAB to create a SqlDatabase Class.

It sounds like the providerName associated with your connection string is either not set or not associated with a known database class, and hence the DAAB cannot create it.

I recommend making sure that you can successfully just call DatabaseFactory.CreateDatabase() to be sure everything is set-up correctly for the DAAB and then incorporate Unity and the Extensions.

Best Regards,

Dave

Oct 8, 2008 at 12:38 PM
The provider name is System.Data.SqlClient.

For this one class (DataAction), if I use DatabaseFactory.CreateDatabase() to create the database instead of using constructor injection, the DAAB works fine.

Any other ideas?

Oct 8, 2008 at 7:57 PM
I just re-read your message above.

Clearly the problem is not with Enterprise Library and the Extension but that you have not registered all the dependencies with Unity.

Make sure you have registered the Adapter and DataAction and anything else that are required dependencies in your dependency chain. Essentially check all your constructors and make sure all those constructor arguments have been registered with Unity and can be resolved.

Regards,

Dave
Oct 8, 2008 at 8:11 PM
Hmm, ok.  I have only registered 1 type in the config file, that is the IAdapter, which is resolved with a call to Resolve<IAdapter>.

I didn't think that other types had to be registered, if they were concrete types.  By that I mean:

Adapter(DataAction da) - this would not require a type registration in Unity, because DataAction is a concrete type.... Unity is only instancing it for me.
Adapter(IDataAction da) - this would require a registration, in order for unity to know which concrete class to return for the IDataAction interface.

Is the above incorrect?  Perhaps I have missed the point somewhere along the way but I've seen several examples where types were automatically resolved without being explicitly registered.

As for the Database class, I figured it would not require a registration, because that is what the Extensions do for you?

Thanks.

Oct 8, 2008 at 10:00 PM
Ok, I'm running in circles here trying different things.  I believe this is the heart of my problem.

When I add the Extensions to the web.config file for the WCF service host, it doesn't even get to the point of trying to resolve my types above. 

It fails on this line:

            section.Containers.Default.Configure(serviceHost.Container);

With this error message:

Unable to find the requested .Net Framework Data Provider.  It may not be installed.

   at System.Data.Common.DbProviderFactories.GetFactory(String providerInvariantName)
   at Microsoft.Practices.EnterpriseLibrary.Data.DatabaseConfigurationView.GetDefaultMapping(String name, String dbProviderName)
   at Microsoft.Practices.EnterpriseLibrary.Data.DatabaseConfigurationView.GetProviderMapping(String name, String dbProviderName)
   at Microsoft.Practices.EnterpriseLibrary.Data.Configuration.Unity.DataAccessBlockExtension.Initialize()
   at Microsoft.Practices.Unity.UnityContainerExtension.InitializeExtension(ExtensionContext context)
   at Microsoft.Practices.Unity.UnityContainer.AddExtension(UnityContainerExtension extension)
   at Microsoft.Practices.Unity.Configuration.UnityContainerExtensionElement.Configure(IUnityContainer container)
   at Microsoft.Practices.Unity.Configuration.UnityContainerElement.Configure(IUnityContainer container)
   at Operitel.LearnFlex.BusinessService.UnityWCFIntegration.UnityServiceHostFactory.CreateServiceHost(Type serviceType, Uri[] baseAddresses) in D:\MyWork\BusinessService\BusinessService.UnityWCFIntegration\UnityServiceHostFactory.cs:line 25
   at System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses)
   at System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath)
   at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(String normalizedVirtualPath)
   at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)


Oct 9, 2008 at 2:57 PM
Is this a bug?  I have narrowed down my problem.

This line (212 of Microsoft.Practices.EnterpriseLibrary.Data.DatabaseConfigurationView):
            DbProviderFactory providerFactory = DbProviderFactories.GetFactory(dbProviderName);

Fails when dbProviderName == "System.Data.EntityClient"
with error:   Unable to find the requested .Net Framework Data Provider.  It may not be installed.

Unlike many examples I worked with, which worked fine, my project started with the Entity Framework, and therefore had 2 connection strings defined.  One I was using for the EF and the other I was using for ADO.NET data access.  This web.config is contained within a WCF service.  Adding a reference to the System.Data.Entity does not solve the problem.

If I remove the Entity Framework connection string from the <connectionStrings> section, then everything works fine from the Unity, Data Access Block point of view.... of course now I can't switch to the Entity Framework DAL, which was sort of the point of using Unity in the first place.

Thoughts?



Oct 10, 2008 at 8:23 PM
Lefty,

We've looked at it and it's a bug. When using the DAAB directly, we only read the connection strings that are specifically requested, which is why going directly to the DAAB works. With the Unity extension, however, to get everything configured correctly we read all the connection strings.

The Entity framework is actually what's doing this wrong. They're specifying the DataProvider, but they're not pointing at an actual ADO.NET data provider, so the Unity code breaks.

We've just checked in a fix in the current code base to ignore the connection strings with "invalid" data providers. Fix will be in the next drop.

-Chris

Oct 15, 2008 at 9:08 PM
That is great.  Thanks for confirming.

So, does this mean the issue is fixed in the latest version checkin - changeset 41652?
Oct 15, 2008 at 9:33 PM
Yes, the change is in yesterday's drop. Of course, we never know if it actually fixes your issue unless you tell us. So please give it a try and let us know.
Oct 22, 2008 at 1:49 PM
Hey Chris, I finally got around to trying this new drop and it does appear to have fixed the problem.  Thanks!
Oct 22, 2008 at 7:15 PM
Hey Chris, perhaps I spoke too soon.

I created a shared assemblies folder in our project structure and it includes the app block assemblies.  I put the 1010 versions in there and added/removed all of our references to be file references that point to the assemblies in this folder.  (no longer using the GAC)

Things looked fine, it builds and runs,  but now when trying to test my app block error handlers, I seem to be getting an error when the ExceptionPolicy.HandleException(ex, policyName) method is called... internally it seems to try to load this assembly?

LOG: DisplayName = VJSharpCodeProvider, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Could that have anything to do with me switching to the new dropped version?
Oct 22, 2008 at 8:23 PM
Ok, nevermind.  I found the problem.  It seems there are lots of reports of this ghost assembly not loading and I believe it was masking my problem.

The actually problem was, when updating my config files, I missed updating a value and had this:
PublicKeyToken=
instead of
PublicKeyToken=null

for the logging assembly.

I'm not sure why the logging assembly didn't show up in the fusion log.  After fixing the config file, things work properly again, however, the "ghost"  VJSharpCodeProvider still logs a problem in the fusion log, however, the code runs without crashing now.