PetaPoco
                                
                                 PetaPoco copied to clipboard
                                
                                    PetaPoco copied to clipboard
                            
                            
                            
                        DbProviderFactory with ORM Profiler
We are using ORM Profiler to examine generated queries from our ORM tools. It works with other ORM such as EF and Dapper but not with PetaPoco. Can you suggest a work-around for this? Apparently PetaPoco is doing something different with DbProviderFactory but I'm not able to understand what that is.
Interesting. All that I can think of is that depending on which method of construction is used will depend on whether the provider factory is used. Maybe this document page will be of use for you?
How did you go @jovball?
I can only get this to work by changing the PetaPoco code and using the Database(IDbConnection connection) overload. The code that needs to change is any code that calls internal static IProvider Resolve(Type type, bool allowDefault, string connectionString). The allowDefault parameter needs to be true or it will throw an exception.
It seems like there should be a property or parameter related to allowDefault because it is false both places in the code and there is no way to change it.
@jovball
I've looked at the code.
It should throw and that is the correct behaviour.
It's throwing because it can detect the database type, and therefore, it doesn't know what the correct dialect is.
Can you post the exception message as it contains useful information?
I understand that the code is doing what it is supposed to do. My point is that there is no way for me to change the allowDefault parameter value so that it falls through to the "default." That is, line 4693 is effectively unreachable code although the compiler cannot know it.
// Assume SQL Server return Singleton<SqlServerDatabaseProvider>.Instance;
As to the exception, ORM Profiler is wrapping the connection so PetaPoco doesn't recognize it.
Message=Could not match SD.Tools.OrmProfiler.Interceptor.ProfilerDbConnection to a provider.
Parameter name: type
StackTrace:  at PetaPoco.DatabaseProvider.Resolve(Type type, Boolean allowDefault, String connectionString) in 
I would suggest that this parameter needs to be exposed somewhere. Along those lines, I'd like to see PetaPoco.cs as a partial class so that I could extend this myself if I wanted to. I'd prefer that to an inherited class.
Hi, I wrote ORM profiler and LLBLGen Pro
The problem you state with database determination based on the factory is real. However, there's a trick we orm writers implement and which is used by Glimpse, Hibernating Rhino's profilers and ORM profiler, we implement IServiceProvider on the wrapping DbProviderFactory. This way you can determine the real provider factory even if it's a wrapped factory. This also helps in the situation where multiple profilers are used (e.g. glimpse and ours or hibernating rhino's) and the factory is wrapped multiple times.
See: http://referencesource42.llblgen.com/#SD.LLBLGen.Pro.ORMSupportClasses/Persistence/DbProviderFactoryInfo.cs,180
This method unwraps the factory received from .NET using IServiceProvider and either keeps the one received (if it's not a wrapping one) or will unwrap it with the IServiceProvider interface. This will get you the real factory no matter how many times it's wrapped with profiler wrappers :)
Hope this helps.
@FransBouma thanks for the guidance, I will add this PetaPoco ASAP.
Also a shutout to your great LLBLGen Pro product. It's featured in a few projects that I've had the privilege to work on in the past, and I've always had good experiences with it. :clap:
@jovball please expect this update in the next week or so. As a work around, you can unwrap the factory before passing it to PetaPoco
That's great. I appreciate the responsiveness from both of you.
@pleb thanks for the kind words and glad I could help out! Keep up the good work :)
@jovball the latest beta builds will unwrap the factory for you. let me know how you go :smile:
By beta, do you mean Install-Package PetaPoco -Pre? Or something else? I'm getting the same results with that package as before. No profiling and an exception at the same place when I use the overload with an open connection.
@jovball
Yep -Pre
The overload to use is the one which takes an instance of DbProviderFactory
Here's a code snippet. I'm not getting anything in ORM Profiler using this code.
var connectionStringKey = "NorthwindConnection"; var connectionString = ConfigurationManager.ConnectionStrings[connectionStringKey].ConnectionString; var factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); var sqlDb = new Database(connectionString, factory); var country = "Brazil"; var customers = sqlDb.Fetch<Customer>("SELECT * FROM dbo.Customers WHERE country=@0", country);
var factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
That appears to be the SQL factory and not the profiler factory
In my code, I use the specific database provider. ORM Profiler should intercept that call and wrap it with it's provider.
It appears that the only PetaPoco.Database overload that will work with ORM Profiler is the one that accepts a connection. That's the overload that I need fixed. I either need PetaPoco to unwrap the provider correctly or give me a way to prevent that exception in the Resolve method.
@jovball does it work for you if you unwrap the provider yourself?
var factory = DbProviderFactories.GetFactory("System.Data.SqlClient");That appears to be the SQL factory and not the profiler factory
That's the thing: that code will produce the original SqlClient factory if there's no profiler active, or it will produce the wrapping factory if there is a profiler active. DbProviderFactory's interface is the same. So the code using the factory is the same, it won't notice the factory is actually wrapped.
Unless, the code checks the type of the factory returned and tries to conclude what DB is in use. Then the factory has to be unwrapped, if necessary. You can use the code I linked to earlier, which simply gives you the original factory if there's a wrapping in play or the factory which was received by GetFactory() if no wrapping is in play. That unwrapped factory is only used for the check. The factory returned by GetFactory() should be used by all the other code, like normal.
@FransBouma Ah ok. I wasn't sure if somebody using a profiler would need to change "System.Data.SqlClient" or not. Thanks for clearing it up.
@jovball I'm not sure what going on. The constructor which takes a factory provider unwraps (beta package only), so if it's not working for you I'm stumped.
Actually I have an idea.
From the looks of it, if I simply call the Database() ctor, I end up here: https://github.com/CollaboratingPlatypus/PetaPoco/blob/development/PetaPoco/Core/DatabaseProvider.cs#L182
which doesn't do any checking properly, or did I miss a line?
@jovball try this
public class ProfilerDatabaseProvider : SqlServerDatabaseProvider
{
    public override DbProviderFactory GetFactory()
    {
        return DbProviderFactories.GetFactory("System.Data.SqlClient");
    }
}
var petapoco = new Database(connectionString, new ProfilerDatabaseProvider());
@FransBouma I think it could be to do with the way PetaPoco gets an instance of a ProviderFactory when using self discovery. See here
Yes, using the code I could navigate it better, I used the github website but I ended up at the wrong spot. I indeed see the SqlServerDatabaseProvider being created, and then the GetFactory() method being called which indeed does the wrong thing.
I don't know why you obtain the factory using the hard-coded factory type at line 20 in SqlServerDatabaseProvider. The DbProviderFactories class already does that for you. Is this done for .NET core? As .NET core has a DbProviderFactory system as well (but it's as broken as .NET core is in general) where the user has to inject the factory using the default DI system. I can imagine you want to avoid that.
If this isn't for .NET core, I'd simply call DbProviderFactories.GetFactory(factory well known name) and be done with it. This also gives the user of your ORM the ability to define a factory in the application's config file, e.g. they want to use a factory defined locally, e.g. one built from source, or not installed in the GAC.