17

I'm writing a reusable base repository class where the developer will pass in a generic representing the ObjectContext and the base repository will create an instance of it with Activator.CreateInstance. When debugging I want to make use of the nuget package CommunityEFProviderWrappers.EFTracingProvider. So my code to setup the object context looks like this:

    public void RenewDataContext()
    {
#if DEBUG
        // get the default container name
        var containerName = Activator.CreateInstance<T>().DefaultContainerName;

        // create an instance of the object context using EF Trace
        Context = (T)Activator.CreateInstance(typeof(T), EFTracingProviderUtils.CreateTracedEntityConnection(containerName));
        Context.EnableTracing();

#else
        Context = Activator.CreateInstance<T>();
#endif
    }

The problem is that this always throws the following error when it tries to create an instance of the ObjectContext with the EFTracingProvider: "Schema specified is not valid. Errors: \r\n(0,0) : error 0175: The specified store provider cannot be found in the configuration, or is not valid."

If I replace containerName with the name of the connection string in the web config and don't do the first Activator.CreateInstance<T>() then it works fine. So the issue has something to do with the fact that I create the first instance and then the second.

Here is what I have tried:

  1. dispose and null out the first instance.
  2. close the connection on the first instance.
  3. put the first instance in a using statement.
  4. explicitly define the assembly containing the ObjectContext in the connection string in the web.config in the startup project (MetadataException when using Entity Framework Entity Connection)

I am trying to avoid having the developer pass in the generic type of the ObjectContext AND the name of the connection string. That seems kind of redundant.

So my question is: How do I get the connection name from the generic representing the object context and still be able to use it to create an instance of the object context using the EntityConnection generated by EF Trace?

My question is about why this method doesn't work, not about possible work arounds.

Community
  • 1
  • 1
bygrace
  • 5,868
  • 1
  • 31
  • 58
  • 1
    Can you share the values of `_defaultContainerName` and `EFTracingProviderUtils.CreateTracedEntityConnection(_defaultContainerName)` ? – scmccart Nov 08 '12 at 17:14
  • _defaultContainerName is only initialized by the code inside of that if statement so it is whatever the DefaultContainerName is for the given ObjectContext represented by T. It is initially null so the code inside that if statement runs when the error occurs. EFTracingProviderUtils.CreateTracedEntityConnection(_defaultContainerName) returns a EntitnyConnection based on the value of _defaultContainerName. Since the goal of all of this is that everything is dynamic the values are dependent on the generic that is passed in. – bygrace Nov 08 '12 at 18:04
  • If your connection string was: Then _defaultContainerName would be "MyDataContext". – bygrace Nov 08 '12 at 18:06
  • Isn't the name simply the type name, `typeof(T).Name`? Or am I oversimplifying? –  Nov 13 '12 at 18:45
  • Not the type name, the connection name. – bygrace Nov 13 '12 at 18:46
  • See the third comment. I want the name of the connection string in the web.config, not the name of the EF class. – bygrace Nov 13 '12 at 18:48
  • Ah, wait, I think I see what you mean. What I was talking about is for `DbContext`-derived contexts. Sorry about that. –  Nov 13 '12 at 18:51
  • Well, it does seem that if you use the wizard then the ObjectContext class name will match the connection string name. But I can't guarantee that someone wont pass in a class that inherits from that object context and thus would have a type name that differed from the connection string name. But if I don't get a better answer I might just go with that. – bygrace Nov 13 '12 at 18:57
  • So, if I'm understanding this properly, you just need the connection string? Or am I missing something? – CodingGorilla Nov 16 '12 at 16:41
  • I was going for the name of the connection string but I think EF Trace takes the whole connection string as well. The key is that I want to get it dynamically off of the generic the represents the Object Context. I would also like to know why what I am doing is throwing an error. – bygrace Nov 16 '12 at 18:36

3 Answers3

1

Here is your solution, add the folowing code to your config file:

<system.data>
  <DbProviderFactories>
    <add name="EF Caching Data Provider"
         invariant="EFCachingProvider"
         description="Caching Provider Wrapper"
         type="EFCachingProvider.EFCachingProviderFactory, EFCachingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
    <add name="EF Tracing Data Provider"
         invariant="EFTracingProvider"
         description="Tracing Provider Wrapper"
         type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
    <add name="EF Generic Provider Wrapper"
         invariant="EFProviderWrapper"
         description="Generic Provider Wrapper"
         type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
  </DbProviderFactories>
</system.data>

The problem occurs in EFTracingProviderUtils.CreateTracedEntityConnection method. It searches for .Net Framework Data Provider - EFTracingProvider, which must be specified in your config file. When it can't find it, it can't build such connection string for its purposes:

"metadata=reader://998e0526-8372-4bf9-83d3-949dececce96; provider=EFTracingProvider; provider connection string=\"wrappedProvider=;data source=.\SQLEXPRESS;attachdbfilename=|DataDirectory|\database.mdf; integrated security=True; connect timeout=30; user instance=True; multipleactiveresultsets=True; App=EntityFramework\";"

As you can see metadata reader specified it this connection string, here will be MetadataException which is thrown when EF can't find EFTracingProvider. It is just problem with third-party method.

You can call next method and receive same result without any changes to your config:

EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(containerName, new string[]{});
Igor Lozovsky
  • 2,275
  • 2
  • 15
  • 14
  • Could you add an explanation of what that is supposed to do and how it would be a comparable workaround for what I am trying to accomplish? – bygrace Mar 18 '13 at 12:27
  • Look at your exception - The specified store provider cannot be found in the configuration, or is not valid. Do you understand, that something is missing in your config file? I hope yes. You can see it in your exception message. I gave you solution, the explanation is in your exception. If you don't know how to use google - look at this article. http://blogs.msdn.com/b/adonet/archive/2010/09/13/ef-caching-with-jarek-kowalski-s-provider.aspx – Igor Lozovsky Mar 18 '13 at 13:58
  • I did a quick test and it does seem to work. Per my question, my issue only occurred when I created an instance of the objectcontext to get the name. If I didn't do that step, and just passed in the name of the connection as a string, then it works fine without the configuration piece you suggested. Do you know why that is? – bygrace Mar 18 '13 at 18:12
  • Hardcoded connection name didn't gave me any positive results when I tested your issue. – Igor Lozovsky Mar 18 '13 at 20:30
  • That last line `EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(containerName, new string[]{});` works perfectly for me. I just wish I understood why. – Nate Cook Jun 22 '13 at 17:52
0

Not really direct answer.

I would not recommend hardcode assumption that such configuration exists:

(T)Activator.CreateInstance(typeof(T), EFTracingProviderUtils.CreateTracedEntityConnection(containerName));

There are quite rare cases (outside of simple examples) when people store EF connection string to the config files under default name. I think it might be better to accept connection string or apply some sort of convention.

FYI. DbContext does not expose DefaultContainerName directly. You need to get ObjectContext for it first.

Mike Chaliy
  • 25,801
  • 18
  • 67
  • 105
  • That makes sense. At my company, for which I wrote this, we always have the connection strings stored in a config file so we will always have a DefaultContainerName. I think I was on EF4 when I wrote this question so I was using ObjectContext, not DbContext. In EF5 I just baked EF Trace with the conditional into the T4 template. – bygrace Feb 21 '13 at 16:10
  • If you could find a way to get the connection string out of an instance of DbContext/ObjectContext and create a new instance using EFTrace without the above issue then I would consider that an accepted answer. The whole point with this was to make it dynamic rather then making the developer pass it in with the ObjectContext. – bygrace Feb 21 '13 at 16:23
0

my friend is so simple that it goes unnoticed, its configuration file is not with the connection string in the same Project view (StartUP Project), if not in the asp.net web.config and if windows form is not in app.config Project (View) windows Form (UI).