Кассини/Веб-сервер. WebDev, NUnit и AppDomainUnloadedException

В.NET Core и.NET> 4 существует универсальный метод синтаксического анализа :

Enum.TryParse("Active", out StatusEnum myStatus);

Это также включает C#7, новый встроенный out переменные, таким образом, это делает синтаксический анализ попытки, преобразование в явный перечислимый тип и initialises+populates myStatus переменная.

, Если у Вас есть доступ к C#7 и последней.NET, это - лучший способ.

Исходный Ответ

В.NET это довольно ужасно (до 4 или выше):

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

я склонен упрощать это с:

public static T ParseEnum(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

Тогда я могу сделать:

StatusEnum MyStatus = EnumUtil.ParseEnum("Active");

Одна опция, предложенная в комментариях, состоит в том, чтобы добавить расширение, которое достаточно просто:

public static T ToEnum(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum();

Наконец, можно хотеть иметь перечисление по умолчанию, чтобы использовать, если строка не может быть проанализирована:

public static T ToEnum(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse(value, true, out result) ? result : defaultValue;
}

, Который делает это вызовом:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

Однако я был бы тщательным добавлением дополнительного метода как это к string как (без управления пространством имен), появится на всех экземплярах [1 110], содержат ли они перечисление или не (таким образом 1234.ToString().ToEnum(StatusEnum.None) было бы допустимо, но бессмыслен). Это часто быть лучшим постараться не создавать помехи базовым классам Microsoft дополнительными методами, которые только применяются в очень определенных контекстах, если у Вашей всей группы разработчиков нет очень хорошего понимания того, что делают те расширения.

9
задан Rasmus Faber 18 February 2009 в 17:44
поделиться

1 ответ

У меня была та же проблема, но я не использовал Кассини. Вместо этого у меня был собственный хостинг веб-сервера на основе System.Net.HttpListener с поддержкой ASP.Net через System.Web.HttpRuntime , работающей в другом домене приложений, созданном через System.Web.Hosting.ApplicationHost.CreateApplicationHost () . По сути, так работает Cassini, за исключением того, что Cassini работает на уровне сокетов и реализует множество функций, предоставляемых самим System.Net.HttpListener .

В любом случае, для решения моей проблемы мне понадобилось вызвать System.Web.HttpRuntime.Close () перед тем, как позволить NUnit выгрузить мой домен приложения.

Мой WebServerHost класс - это очень маленький класс, который позволяет маршалировать запросы в домен приложения, созданный System.Web.Hosting.ApplicationHost.CreateApplicationHost () .

using System;
using System.IO;
using System.Web;
using System.Web.Hosting;

public class WebServerHost :
    MarshalByRefObject
{
    public void
    Close()
    {
        HttpRuntime.Close();
    }

    public void
    ProcessRequest(WebServerContext context)
    {
        HttpRuntime.ProcessRequest(new WebServerRequest(context));
    }
}

using System;
using System.IO;
using System.Web;
using System.Web.Hosting;

public class WebServerHost :
    MarshalByRefObject
{
    public void
    Close()
    {
        HttpRuntime.Close();
    }

    public void
    ProcessRequest(WebServerContext context)
    {
        HttpRuntime.ProcessRequest(new WebServerRequest(context));
    }
}

Класс WebServerContext - это просто оболочка вокруг экземпляра System.Net.HttpListenerContext , производного от System.MarshalByRefObject, чтобы разрешить вызовам из нового домена хостинга ASP.Net для обратного вызова в мой домен.

using System;
using System.Net;

public class WebServerContext :
    MarshalByRefObject
{
    public
    WebServerContext(HttpListenerContext context)
    {
        this.context = context;
    }

    //  public methods and properties that forward to HttpListenerContext omitted

    private HttpListenerContext
    context;
}

Класс WebServerRequest - это просто реализация абстрактного класса System.Web.HttpWorkerRequest , который обращается в мой домен из домена хостинга ASP.Net через WebServerContext WebServerContext .

using System;
using System.IO;
using System.Web;

class WebServerRequest :
    HttpWorkerRequest
{
    public
    WebServerRequest(WebServerContext context)
    {
        this.context = context;
    }

    //  implementation of HttpWorkerRequest methods omitted; they all just call
    //  methods and properties on context

    private WebServerContext
    context;
}

Класс WebServer - это контроллер для запуска и остановки веб-сервера. Когда началось, домен хостинга ASP.Net создается с моим классом WebServerHost в качестве прокси для обеспечения взаимодействия. Также запускается экземпляр System.Net.HttpListener , и запускается отдельный поток для приема соединений. Когда соединение установлено, рабочий поток запускается в пуле потоков для обработки запроса, снова через мой класс WebServerHost . Наконец, когда веб-сервер останавливается, слушатель останавливается, контроллер ожидает выхода потока, принимающего соединения, а затем слушатель закрывается. Наконец, среда выполнения HTTP также закрывается через вызов метода WebServerHost.Close () .

using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Web.Hosting;

class WebServer
{
    public static void
    Start()
    {
        lock ( typeof(WebServer) )
        {
            //  do not start more than once
            if ( listener != null )
                return;

            //  create web server host in new AppDomain
            host =
                (WebServerHost)ApplicationHost.CreateApplicationHost
                (
                    typeof(WebServerHost),
                    "/",
                    Path.GetTempPath()
                );

            //  start up the HTTP listener
            listener = new HttpListener();
            listener.Prefixes.Add("http://*:8182/");
            listener.Start();

            acceptConnectionsThread = new Thread(acceptConnections);
            acceptConnectionsThread.Start();
        }
    }

    public static void
    Stop()
    {
        lock ( typeof(WebServer) )
        {
            if ( listener == null )
                return;

            //  stop listening; will cause HttpListenerException in thread blocked on GetContext()  
            listener.Stop();

            //  wait connection acceptance thread to exit
            acceptConnectionsThread.Join();
            acceptConnectionsThread = null;

            //  close listener
            listener.Close(); 
            listener = null;

            //  close host
            host.Close();
            host = null;
        }
    }

    private static WebServerHost
    host = null;

    private static HttpListener
    listener = null;

    private static Thread
    acceptConnectionsThread;

    private static void
    acceptConnections(object state)
    {
        while ( listener.IsListening )
        {
            try
            {
                HttpListenerContext context = listener.GetContext();
                ThreadPool.QueueUserWorkItem(handleConnection, context);
            }
            catch ( HttpListenerException e )
            {
                //  this exception is ignored; it will be thrown when web server is stopped and at that time
                //  listening will be set to false which will end the loop and the thread
            }
        }
    }

    private static void
    handleConnection(object state)
    {
        host.ProcessRequest(new WebServerContext((HttpListenerContext)state));
    }
}

Наконец, этот класс Initialization , помеченный NUnit [SetupFixture ], используется для запуска веб-сервера при запуске модульных тестов,

7
ответ дан 4 December 2019 в 23:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: