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

时间:2013-09-30 00:47:40

标签: c# .net .net-4.5

我有一个 love 的功能,可以利用“正确的”方式执行.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
}

我能够找到完成这项工作的唯一方法是 所有人 ,他们将此共享代码用于alter their Visual Studio solution

哪个不会发生;它必须正常工作,否则根本不会被使用。

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

换句话说,如果编译好的话会很棒:

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

但如果目标框架太低,它就无法编译。

3 个答案:

答案 0 :(得分:4)

如果优先级是使用最少的编译时间设置,那么我只需将检查移动到运行时并使用反射来检查方法是否可用并在不是时使用变通方法。这是一个额外的好处,在安装了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的要求可以接受,则代码可以更漂亮。

答案 1 :(得分:1)

此答案与标记答案相同,但对于寻找不基于用户原始帖子方案的解决方案的其他人更容易理解。

使用反射来确定类是否存在。如果是,则动态创建并使用它,否则使用可为该场景定义的变通类或代码。

以下是我用于AggregateException的代码,仅用于.Net 4及更高版本:

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.

答案 2 :(得分:0)

只要代码不是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'(不确定),它可能无效。
  • 你必须设置更高的目标框架并且要非常小心,避免错误地依赖新方法。