views:

244

answers:

3

In .NET 4 Code Access Security (CAS) is deprecated. Whenever you call a method that implicitly uses it, it fails with a NotSupportedException, that can be resolved with a configuration switch that makes it fall back to the old behavior.

We have a common library that's used in both .NET 3.5 and .NET 4, so we need to be able to tell whether or not we should use the CAS method.

For example, in .NET 3.5 I should call:

Assembly.Load(string, Evidence);

Whereas in .NET 4 I want to call

Assembly.Load(string);

Calling Load(string, Evidence) throws a NotSupportedException.

Of course this works, but I'd like to know if there's a better method:

try
{
    asm = Assembly.Load(someString, someEvidence);
}
catch(NotSupportedException)
{
    asm = Assembly.Load(someString);
}
+2  A: 

HI, Using Environment.Version.Major and Environment.Version.Minor should solve the issue.

Version v = Environment.Version;
if (Environment.Version.Major <= 3)
{
    //DO 3.5 here
}
else if (Environment.Version.Major >= 4)
{
     //DO 4 here
}

Hope this helps.

Edit: Changed conditions to assume the same CAS will be implemented on future versions of .NET.

Luis
This is a great way to write code that fails miserably when the customer installs .NET 5.x on their machine in a couple years.
Ben Voigt
Thanks for the comment, didn't notice mayor bug.
Luis
just `else { }` would've been enough :-) If I'm going with this, I'll use `if ( ... < 4) { // DO 3.5 } else { // DO 4.0 + }`
Sander Rijken
A: 

The getter for the System.Security.HostSecurityManager.DomainPolicy property is a public, lightweight method that will fail fast in .NET 4.0 if the legacy CAS policy switch is not applied. You may want to consider setting up a helper class to allow you to avoid incurring the cost of the potential exception more than once. e.g.:

internal static class CasPolicyHelper
{
    private static bool? _isCasPolicyEnabled;

    internal static bool IsCasPolicyEnabled
    {
        get
        {
            if (_isCasPolicyEnabled == null)
            {
                HostSecurityManager hostSecurityManager = new HostSecurityManager();
                try
                {
                    PolicyLevel level = hostSecurityManager.DomainPolicy;
                    _isCasPolicyEnabled = true;
                }
                catch (NotSupportedException)
                {
                    _isCasPolicyEnabled = false;
                }
            }

            return _isCasPolicyEnabled.Value;
        }
    }
}
Nicole Calinoiu
I guess this'll work too. I don't really like causing and catch exceptions for finding out wether or not a setting is enabled. It's better than the version check because it actually checks the feature, and it's worse because it causes exceptions. Haven't decided yet what the best solution to this is.
Sander Rijken
If you prefer reflecting into undocumented low-visibility code, you could always check the existence and value of System.AppDomain.IsLegacyCasPolicyEnabled. IMO, much worse than a one-time exception, but ymmv...
Nicole Calinoiu
+1  A: 

This codes directly against the feature.

    public static Assembly LoadAssembly(string assembly, Evidence evidence)
    {
        Assembly asm;
        MethodInfo load = 
            typeof(Assembly).GetMethod("Load", 
                                        new Type[] {typeof(string), typeof(Evidence)});

        if (Attribute.IsDefined(load, typeof(ObsoleteAttribute)))
        {
            asm = Assembly.Load(assembly);
        }
        else
        {
            asm = Assembly.Load(assembly, evidence);
        }
        return asm;
    }

This code assumes the following using statements.

using System;
using System.Reflection;
using System.Security.Policy;

And if this will be called frequently you could avoid the reflection performance hit with something like this.

private static bool? _isEvidenceObsolete = null;
public static Assembly AssemblyLoader(string assembly, Evidence evidence)
{
    Assembly asm;
    if (!_isEvidenceObsolete.HasValue)
    {
        MethodInfo load =
           typeof(Assembly).GetMethod("Load",
                                       new Type[] { typeof(string), typeof(Evidence) });
        _isEvidenceObsolete = Attribute.IsDefined(load, typeof(ObsoleteAttribute));
    }

    if (_isEvidenceObsolete.Value)
    {
        asm = Assembly.Load(assembly);
    }
    else
    {
        asm = Assembly.Load(assembly, evidence);
    }
    return asm;
}

Edit: I had to see for myself what the performance statistics would be, And this is what I got.

Elapsed time in milliseconds:

Catch Exception: 45331
Reflection: 58
Static Reflection: 1

Here is the code I used to do the benchmarking:

public static void BenchmarkLoaders()
{
    Stopwatch timer = new Stopwatch();

    // Benchmark catching Exceptions
    timer.Start();
    for (int i = 0; i < 10000; i++)
    {
        NotSupported notSupported = new NotSupported();
        try
        {
            notSupported.ThrowException("Obsoleted Method Call");
        }
        catch (NotSupportedException nse)
        {
            //Do something
        }
    }
    timer.Stop();
    Console.WriteLine("Catch Exception: {0}", timer.ElapsedMilliseconds);
    timer.Reset();

    // Benchmark Reflection
    timer.Start();
    for (int i = 0; i < 10000; i++)
    {
        NotSupported notSupported = new NotSupported();

        notSupported.ReflectAssembly();
    }
    timer.Stop();
    Console.WriteLine("Reflection: {0}", timer.ElapsedMilliseconds);
    timer.Reset();


    // Benchmark Static Reflection
    timer.Start();
    for (int i = 0; i < 10000; i++)
    {
        NotSupported.ReflectAssemblyStatic();
    }
    timer.Stop();
    Console.WriteLine("Static Reflection: {0}", timer.ElapsedMilliseconds);
    timer.Reset();

}

This is the NotSupported class.

public class NotSupported
{
    public void ThrowException(string message)
    {
        throw new NotSupportedException(message);
    }

    public void ReflectAssembly()
    {
        MethodInfo load = 
            typeof(Assembly).GetMethod("Load", 
                                        new Type[] { typeof(string), typeof(Evidence) });

        if (Attribute.IsDefined(load, typeof(ObsoleteAttribute)))
        {
            // Do something
        }
    }

    private static bool? _isEvidenceObsolete = null;
    public static void ReflectAssemblyStatic()
    {
        Assembly asm;
        if (!_isEvidenceObsolete.HasValue)
        {
            MethodInfo load =
               typeof(Assembly).GetMethod("Load",
                                           new Type[] { typeof(string), typeof(Evidence) });
            _isEvidenceObsolete = Attribute.IsDefined(load, typeof(ObsoleteAttribute));
        }

        if (_isEvidenceObsolete.Value)
        {
            //Do Stuff
        }
    }
}

I realize that these aren't real world numbers but it does provide a very compelling argument for the use of reflection over exceptions.

VoidDweller
Thanks for the performance comparison, you proved exactly why I didn't want to go with exceptions in the first place. I ended up checking the `Environment.Version.Major` number, so that's why I accepted that answer, too bad I can't accept both answers
Sander Rijken