Получение имени сборки для процесса, запущенного в домене приложения

У меня есть служба, которая создает домен приложения и запускает его:

this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();

Это уже давно работает хорошо. Проблема заключается в том, что контроллер, запущенный в этом домене приложения, вызывает класс ведения журнала фреймворка. Регистратор получает имя сборки записи и записывает его как источник в журнале событий. Вот как регистратор получает имя источника (вызывающего):

private static string GetSource()
{
    try
    {
        var assembly = Assembly.GetEntryAssembly();

        // GetEntryAssembly() can return null when called in the context of a unit test project.
        // That can also happen when called from an app hosted in IIS, or even a windows service.
        if (assembly == null)
        {
            // From http://stackoverflow.com/a/14165787/279516:
            assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
        }

        return assembly.GetName().Name;
    }
    catch
    {
        return "Unknown";
    }
}

Прежде чем добавить эту проверку if, регистратор записывает «Неизвестно» для источника. После некоторых исследований я добавил эту попытку в блок if. Теперь регистратор записывает «mscorlib» в качестве источника (имя сборки записи).

Это обзор: Хост -> Контроллер (работает в домене приложения)

Как я могу получить имя сборки (с контроллером), работающей в домене?

Примечание. Я также пробовал это (ниже), но это дает мне имя фреймворка, в котором существует класс ведения журнала (а не имя сборки, в которой работает контроллер в домене приложения):

assembly = Assembly.GetExecutingAssembly();

person Bob Horn    schedule 11.06.2013    source источник
comment
Избегайте поиска лазейки, чтобы получить информацию, которая тривиально и точно предоставляется вашим кодом. Добавьте свойство.   -  person Hans Passant    schedule 11.06.2013
comment
Так что в основном передать строку методу журнала? Logger.Log("MyApp", message);   -  person Bob Horn    schedule 11.06.2013
comment
Этот подход не работает, когда ведение журнала выполняется на уровне бизнес-логики и может быть вызвано из нескольких потребителей. Потребители знают свое имя, но тогда каждый должен будет передать эту информацию в логические методы. Вместо того, чтобы распространять такую ​​информацию, я действительно хотел бы просто получить ее в одном месте.   -  person Bob Horn    schedule 12.06.2013


Ответы (2)


Возможно, это один из способов сделать то, что вы хотите. Здесь я демонстрирую передачу и получение метаданных в созданный AppDomain с помощью методов SetData и GetData, поэтому не обращайте внимания на то, как я создаю фактический удаленный тип.

using System;
using System.Reflection;

namespace ConsoleApplication13
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain appDomain = AppDomain.CreateDomain("foo");

            appDomain.SetData(FooUtility.SourceKey, FooUtility.SourceValue);

            IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);

            foo.DoSomething();
        }
    }

    public static class FooUtility
    {
        public const string SourceKey = "Source";
        public const string SourceValue = "Foo Host";
    }

    public interface IFoo
    {
        void DoSomething();
    }

    public class Foo : MarshalByRefObject, IFoo
    {
        public void DoSomething()
        {
            string source = AppDomain.CurrentDomain.GetData(FooUtility.SourceKey) as string;

            if (String.IsNullOrWhiteSpace(source))
                source = "some default";

            Console.WriteLine(source);
        }
    }
}

Что выводит:

Foo Host
Нажмите любую клавишу, чтобы продолжить...

Итак, в вашем случае вы можете передать любые исходные метаданные в AppDomain:

this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);

this._appDomain.SetData("Source", "MyController");

this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();

и в вашем методе GetSource проверьте его существование.

private static string GetSource()
{
    try
    {
        string source = AppDomain.CurrentDomain.GetData("Source") as string;

        if (!String.IsNullOrWhiteSpace(source))
            return source;

        var assembly = Assembly.GetEntryAssembly();

        // GetEntryAssembly() can return null when called in the context of a unit test project.
        // That can also happen when called from an app hosted in IIS, or even a windows service.
        if (assembly == null)
        {
            // From http://stackoverflow.com/a/14165787/279516:
            assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
        }

        return assembly.GetName().Name;
    }
    catch
    {
        return "Unknown";
    }
}

ОБНОВИТЬ АЛЬТЕРНАТИВ

Вы также можете объявить метод открытого интерфейса для установки источника в статическом месте в целевом домене.

using System;
using System.Reflection;

namespace ConsoleApplication13
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain appDomain = AppDomain.CreateDomain("foo");

            IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);

            foo.SetSource("Foo Host");

            foo.DoSomething();
        }
    }

    public interface IFoo
    {
        void DoSomething();
        void SetSource(string source);
    }

    public class Foo : MarshalByRefObject, IFoo
    {
        public void DoSomething()
        {
            string source = Foo.Source;

            if (String.IsNullOrWhiteSpace(source))
                source = "some default";

            Console.WriteLine(source);
        }

        public static string Source{get; private set;}

        public void SetSource(string source)
        {
            Foo.Source = source;
        }
    }
}
person Jim    schedule 12.06.2013

Я столкнулся с ситуацией, когда где-то в коде .net он полагался на Assembly.GetEntryAssembly(). Он возьмет возвращенную сборку и проверит ее на наличие атрибута уровня сборки. Что потерпит неудачу, если код находится в домене приложения.

Короче говоря, мне пришлось работать над этой же проблемой. Решение уродливое, я ненавижу, что мне нужно было это сделать, но это сработало...

Если вы читаете документы здесь - Метод Assembly.GetEntryAssembly()

Он содержит этот раздел:

Возвращаемое значение

Тип: System.Reflection.Assembly

Сборка, которая является исполняемым процессом в домене приложения по умолчанию, или первый исполняемый файл, который был выполнен AppDomain.ExecuteAssembly. Может возвращать значение null при вызове из неуправляемого кода.

Чтобы обойти это, я добавил в свой exe код, который завершает процесс, если /initializingappdomain передается в качестве аргумента.

Вот код для этого...

// 1. Create your new app domain...
var newDomain = AppDomain.CreateDomain(...);
            
// 2. call domain.ExecuteAssembly, passing in this process and the "/initializingappdomain" argument which will cause the process to exit right away
newDomain.ExecuteAssembly(GetProcessName(), new[] { "/initializingappdomain" });

private static string GetProcessName()
{
   return System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName.Replace(".vshost", ""));
}

// 3. Use your app domain as you see fit, Assembly.GetEntryAssembly will now return this hosting .net exe.

Опять же, это далеко от идеала. Есть лучшие решения, если вы можете избежать этого, но если вы окажетесь в ситуации, когда вы не владеете кодом, полагающимся на Assembly.GetEntryAssembly(), это поможет вам в крайнем случае.

person Steve Sheldon    schedule 28.04.2015