如何基于 .NET 框架版本定义离开代码

本文关键字:定义 离开 代码 版本 框架 何基于 NET | 更新日期: 2023-09-27 18:33:12

我有一个函数,它希望利用.NET 4.5中提供的"正确"方法来执行某些操作:

public DbDataAdapater CreateDataAdapter(DbConnection connection)
{
   #IFDEF (NET45)
      return DbProviderFactories.GetFactory(connection).CreateDataAdapter();
   #ELSE
      //We can't construct an adapter directly
      //So let's run around the block 3 times, before potentially crashing
      DbDataAdapter adapter; 
      if (connection is System.Data.SqlClient.SqlConnection)
         return new System.Data.SqlClient.SqlDataAdapter();
      if (connection is System.Data.OleDb.OleDbConnection)
         return new System.Data.OleDb.OleDbDataAdapter();
      if (connection is System.Data.Odbc.OdbcConnection)
         return new System.Data.Odbc.OdbcDataAdapter();
      //Add more DbConnection kinds as they become invented
      if (connection is SqlCeConnection)
         return new SqlCeDataAdapter();
      if (connection is MySqlConnection)
         return new MySqlDataAdapter();
      if (connection is DB2Connection)
         return new DB2DataAdapter();
      throw new Exception("[CreateDataAdapter] Unknown DbConnection type: " + connection.GetType().FullName);
   #END
}

我能够找到的唯一方法来完成这项工作是让每个使用此共享代码来更改其 Visual Studio 解决方案的人。

这不会发生;它必须只是工作,否则它根本不会被使用。

当解决方案面向早期版本的 .NET 框架时,是否有办法定义非功能性代码?

换句话说,如果编译它:

public DbDataAdapter CreateDataAdapter(DbConnection conn)
{
   if (System.Runtime.Version >= 45)
      return DbProviderFactories.GetFactor(connection).CreateDataAdapter();
   else
   {
      //...snip the hack...
   }
}

但是,如果目标框架太低,它就不会编译。

如何基于 .NET 框架版本定义离开代码

如果优先考虑的是让它以最少的编译时间设置工作,那么我只会将检查移动到运行时并使用反射来检查该方法是否可用,并在不可用时使用解决方法。这是额外的好处,即面向在安装了 4.5 的客户端中运行的 .NET 4.0 的应用程序将使用更好的方法。

样本:

static Func<DbConnection, DbProviderFactory> GetFactoryDelegate;
private static void Main() {
    Console.WriteLine(GetFactory(new SqlConnection()).CreateDataAdapter());
}
private static DbProviderFactory GetFactory(DbConnection connection) {
    if (GetFactoryDelegate == null) {
        var frameworkGetFactoryMethod = typeof (DbProviderFactories).GetMethod(
            "GetFactory", BindingFlags.Static | BindingFlags.Public,
            null, new[] { typeof (DbConnection) }, null);
        if (frameworkGetFactoryMethod != null) {
            GetFactoryDelegate = (Func<DbConnection, DbProviderFactory>)
                Delegate.CreateDelegate(
                    typeof(Func<DbConnection, DbProviderFactory>),
                    frameworkGetFactoryMethod);
        }
        else { GetFactoryDelegate = GetFactoryThroughWorkaround; }
    }
    return GetFactoryDelegate(connection);
}
private static DbProviderFactory GetFactoryThroughWorkaround(
    DbConnection connection) {
    if (connection is SqlConnection)
        return SqlClientFactory.Instance;
    // ... Remaining cases
    throw new NotSupportedException();
}

这种方法与 JavaScript 世界中当前的最佳实践非常相似,即检查功能是否可用,而不是执行浏览器嗅探。.NET 对应项由于需要使用反射而没有相同的优雅。但是,如果dynamic的要求可以接受,代码可以做得更漂亮。

这个答案与标记的答案相同,但对于寻找不基于用户原始帖子场景的解决方案的其他人来说更容易消化。


使用反射来确定类是否存在。如果是,则动态创建和使用它,否则使用可为该方案定义的解决方法类或代码。

这是我用于 .Net 4 及更高版本的AggregateException的代码:

var aggregatException = Type.GetType("System.AggregateException");
if (aggregatException != null) // .Net 4 or greater
{
    throw ((Exception)Activator.CreateInstance(aggregatException, ps.Streams.Error.Select(err => err.Exception)));
}
// Else all other non .Net 4 or less versions
throw ps.Streams.Error.FirstOrDefault()?.Exception 
      ?? new Exception("Powershell Exception Encountered."); // Sanity check operation, should not hit.

只要代码不是 JIT 的,它就可以引用不存在的方法/类/程序集。由于 JIT 的最小单位是整个函数,因此您需要放置引用函数中可能缺少的方法/类的代码,并动态决定何时调用该函数:

public DbDataAdapter CreateDataAdapter(DbConnection conn)
{
   if (System.Runtime.Version >= 45)
   {
      return Hide45DependencyFromJit(connection);
   }
   else
   {
      //...snip the hack...
   }
}
private void Hide45DependencyFromJit(... connection)
{
      return DbProviderFactories.GetFactor(connection).CreateDataAdapter();
}

笔记:

  • 我不确定 4/4.5 框架是否存在问题,这种方法适用于其他"缺少功能"的情况。
  • 如果Ngen'ed(不确定),它可能不起作用。
  • 您必须将目标框架设置得更高,并且要非常小心,避免错误地依赖新方法。