RickStrahl / Westwind.Utilities

A general purpose utility and helper library for .NET development
MIT License
256 stars 61 forks source link

DbProviderFactory #4

Closed toschug closed 6 years ago

toschug commented 6 years ago

Your blog post about DbProviderFactory brought me here as I am trying to get a lib to .NET Standard. After some (unsuccesful) testing: Is it possible, that there is some kind of variable "mismatch" in DataUtils.GetDbProviderFactory regarding providername and providerName?

The exception I get is: System.NotSupportedException: 'Unsupported Provider Factory specified: System.Data.SqlClient'

I replaced providername/providerName with _lower to make it clearer what I mean:

public static DbProviderFactory GetDbProviderFactory(string providerName)
{
#if NETFULL
    return DbProviderFactories.GetFactory(providerName);
#else
    var _lower = providerName.ToLower();

    if (_lower == "system.data.sqlclient")
        return GetDbProviderFactory(DataAccessProviderTypes.SqlServer);
    if (_lower == "system.data.sqlite" || _lower == "microsoft.data.sqlite")
        return GetDbProviderFactory(DataAccessProviderTypes.SqLite);
    if (_lower == "mysql.data.mysqlclient" || _lower == "mysql.data")
        return GetDbProviderFactory(DataAccessProviderTypes.MySql);
    if (_lower == "npgsql")
        return GetDbProviderFactory(DataAccessProviderTypes.PostgreSql);

    throw new NotSupportedException(string.Format(Resources.UnsupportedProviderFactory,providerName));
#endif
}

For now I ripped off this bit into "my" lib for a first workaround (and to see that it could be that mismatch):

    DbProviderFactory GetFactory(string providerName)
    {
        var _lower = providerName.ToLower();

        if (_lower == "system.data.sqlclient")
            return SqlClientFactory.Instance;

        return null;
    }

I hope it is some help and that I don't misunderstand something completly. Thanks anyway (and for the blog post ;) )

RickStrahl commented 6 years ago

Make sure you have a reference added to System.Data.SqlClient? It should be there if you're using this library but if you use your own code you have to make sure you add the dependency to your project.

toschug commented 6 years ago

I am aware of this, but thanks for pointing this out. I even added System.Data.SqlClient explicit although it gets already referenced by WestWind.Utilities. Same exception.

What I am trying to say is that when I pass the string parameter "System.Data.SqlClient" to DataUtils.GetDbProviderFactory then it will run straight into the NotSupportedException. First, the value of providerName (with uppercase N in name) gets converted to lower case and assigned to providername (wither lowercase n in name). Later on the uppercase variant (providerName) gets checked against the lowercase hardcoded strings (like "system.data.sqlclient"). So it can never evaluate to true and thus falls through to the Exception. The only time when it should work is against MySql.Data/mysql.data.

I hope it explains it better what I mean, sorry for confusion.

Excerpt from master branch (https://raw.githubusercontent.com/RickStrahl/Westwind.Utilities/master/Westwind.Utilities/Utilities/DataUtils.cs) with some comments (UPPERCASE / LOWERCASE):

                                                            // UPPERCASE
  public static DbProviderFactory GetDbProviderFactory(string providerName)
        {
#if NETFULL
            return DbProviderFactories.GetFactory(providerName);
#else
           // LOWERCASE
            var providername = providerName.ToLower();

           // UPPERCASE     <->     LOWERCASE -> always false
            if (providerName == "system.data.sqlclient")
                return GetDbProviderFactory(DataAccessProviderTypes.SqlServer);
            if (providerName == "system.data.sqlite" || providerName == "microsoft.data.sqlite")
                return GetDbProviderFactory(DataAccessProviderTypes.SqLite);
                                                            // LOWERCASE<->LOWERCASE -> true!
            if (providerName == "mysql.data.mysqlclient" || providername == "mysql.data")
                return GetDbProviderFactory(DataAccessProviderTypes.MySql);            
            if (providerName == "npgsql")
                return GetDbProviderFactory(DataAccessProviderTypes.PostgreSql);            

            throw new NotSupportedException(string.Format(Resources.UnsupportedProviderFactory,providerName));
#endif
        }
pkos-xonrg commented 6 years ago

I ran into this same problem just a moment ago, looked at the source and noticed the same issue. I was about to write up a ticket and it looks like @toschug beat me by a couple days. You're converting the providerName to lower case as providername and then using the unconverted providerName parameter passed to the method to compare against your lower case strings in most of the conditions.

I'd recommend using the string Equals with the IgnoreCase option rather than converting your string, as well.

        public static DbProviderFactory GetDbProviderFactory(string providerName)
        {
#if NETFULL
            return DbProviderFactories.GetFactory(providerName);
#else
            if (providerName.Equals("system.data.sqlclient", StringComparison.OrdinalIgnoreCase))
                return GetDbProviderFactory(DataAccessProviderTypes.SqlServer);
            if (providerName.Equals("system.data.sqlite", StringComparison.OrdinalIgnoreCase) ||
                providerName.Equals("microsoft.data.sqlite", StringComparison.OrdinalIgnoreCase))
                return GetDbProviderFactory(DataAccessProviderTypes.SqLite);
            if (providerName.Equals("mysql.data.mysqlclient", StringComparison.OrdinalIgnoreCase) ||
                providerName.Equals("mysql.data", StringComparison.OrdinalIgnoreCase))
                return GetDbProviderFactory(DataAccessProviderTypes.MySql);
            if (providerName.Equals("npgsql", StringComparison.OrdinalIgnoreCase))
                return GetDbProviderFactory(DataAccessProviderTypes.PostgreSql);

            throw new NotSupportedException(string.Format(Resources.UnsupportedProviderFactory,providerName));
#endif
        }
RickStrahl commented 6 years ago

Thanks for the catch on this... I made the fix comparing to the proper string. Duh :-)

I avoided individual Equals calls because there are a lot of them to avoid overhead of many comparisons. Probably negligible but old habits and all...