Гость
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Возможно ли через прокси получать информацию о пользовательских атрибутах класса? / 19 сообщений из 19, страница 1 из 1
26.04.2019, 19:13
    #39806975
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Доброго времени суток.

.Net Framework 4.8. Можно ли как-то сделать так, чтобы прокси, получаемые для объектов, инициализированных в другом домене (AppDomain), имитировали не только методы и свойства целевого объекта, но и предоставляли информацию о пользовательских атрибутах класса?

Я объявил атрибут:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
// Сборка AntCore.dll (для совместного использования приложением и плагинами)
using System;

namespace Ant.Core
{
    [Serializable]
    [AttributeUsage(AttributeTargets.Class)]
    public sealed class CommandIdAttribute: Attribute
    {
        public CommandIdAttribute(string guid)
        {
            Id = Guid.Parse(guid);
        }

        public Guid Id { get; }
    }
}



Затем класс, его использующий:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
// Сборка AntPluginExample1.dll
using System;
using System.Threading;
using Ant.Core;
using static System.Console;
namespace AntPluginExample1
{
    [Serializable]
    [CommandId("{382F5A14-9A96-4A05-80B3-10FE170B59DE}")]
    public sealed class HelloWorld : MarshalByRefObject, ICommand
    {
        public void Dispose()
        {
            WriteLine("Command disposed");
        }

        public void Execute()
        {
            WriteLine($"Domain: {Thread.GetDomain().FriendlyName}");
        }
    }
}



В консольном приложении загружаю сборку AntPluginExample1.dll в отдельный домен. Затем создаю экземпляр ICommand и пытаюсь посмотреть кастомные атрибуты у типа:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
// Приложение, загружающее каждый плагин в свой AppDomain 
// и использующий его команды.
using System;
using System.Linq;
using System.Threading;
using Ant.Core;
using static System.Console;

namespace Ant.CLI
{
    class Program
    {
        static void Main(string[] args)
        {
            Title = "Ant CLI";
            WriteLine($"Current domain: {Thread.GetDomain().FriendlyName}");
            var domain = AppDomain.CreateDomain("AD #2", null, null);
            using (var command = (ICommand)domain.CreateInstanceAndUnwrap(
                "AntPluginExample1, version=1.0.0, culture=neutral, PublickKeyTocken=null",
                "AntPluginExample1.HelloWorld"))
            {
                command.Execute();
                var t = command.GetType();
                WriteLine($"command type: {t}");
                WriteLine("Attributes:");
                foreach(var att in t.CustomAttributes)
                {
                    WriteLine($"\t{att.AttributeType.Name}");
                }
            }
            AppDomain.Unload(domain);
            WriteLine("Press ENTER for exit...");
            ReadKey();
        }
    }
}



Однако вижу, что по факту у сгенерированного прокси присутствует только один атрибут - SerializableAttribute:

Код: powershell
1.
2.
3.
4.
5.
6.
7.
Current domain: AntCLI.exe
Domain: AD #2
command type: AntPluginExample1.HelloWorld
Attributes:
        SerializableAttribute
Command disposed
Press ENTER for exit...



Можно ли как-то сделать так, чтобы прокся имитировала и атрибуты?

С уважением, Андрей
...
Рейтинг: 0 / 0
28.04.2019, 00:41
    #39807245
hVostt
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Compositum,

Нет. Вы грузите из внешней сборки конкретный тип по текстовому полному имени. Атрибут это тоже конкретный тип, который мало того, что вы не грузите, так и ещё не знаете о нём, он может лежать где угодно.

Во-вторых, решение лежит на поверхности:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
    public interface ICommand
    {
        public void Dispose();
        public void Execute();
        public Guid GetCommandId();
    }

    public abstract class CommandBase : MarshalByRefObject, ICommand
    {
       public virtual Guid GetCommandId()
       {
           this.GetType().GetCustomAttribute<CommandIdAttribute>()?.Id ?? default(Guid);
       }

       public abstract void Dispose();
       public abstract void Execute();
    }


    [Serializable]
    [CommandId("{382F5A14-9A96-4A05-80B3-10FE170B59DE}")]
    public sealed class HelloWorld : CommandBase
    {
        public override void Dispose()
        {
            WriteLine("Command disposed");
        }

        public override void Execute()
        {
            WriteLine($"Domain: {Thread.GetDomain().FriendlyName}");
        }
    }



Получаем ID:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
using System;
using System.Linq;
using System.Threading;
using Ant.Core;
using static System.Console;

namespace Ant.CLI
{
    class Program
    {
        static void Main(string[] args)
        {
            Title = "Ant CLI";
            WriteLine($"Current domain: {Thread.GetDomain().FriendlyName}");
            var domain = AppDomain.CreateDomain("AD #2", null, null);
            using (var command = (ICommand)domain.CreateInstanceAndUnwrap(
                "AntPluginExample1, version=1.0.0, culture=neutral, PublickKeyTocken=null",
                "AntPluginExample1.HelloWorld"))
            {
                command.Execute();
                var commandId = command.GetCommandId();
            }
            AppDomain.Unload(domain);
            WriteLine("Press ENTER for exit...");
            ReadKey();
        }
    }
}



Попробуйте, я не проверял :)
...
Рейтинг: 0 / 0
28.04.2019, 11:29
    #39807281
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
hVosttАтрибут это тоже конкретный тип, который мало того, что вы не грузите, так и ещё не знаете о нём, он может лежать где угодно.
Ну почему же не знаю? Ведь я же сборку, в которой определены атрибут CommandIdAttribute и интерфейс ICommand , загружаю как в основной домен приложения (в методе Main() я выполнял явное приведение прокси к типу ICommand ), так и в тот AppDomain , с которым взаимодействую посредством прокси. Т.е. эти типы известны по обе стороны.

hVosttПопробуйте, я не проверял :)

Спасибо, :) Я тоже думал о подобном решении, но надеялся, что получать информацию об атрибутах из проксей каким-то образом всё же можно. Да, наверное так и придётся поступать. Ещё раз спасибо за ответ :)
...
Рейтинг: 0 / 0
28.04.2019, 16:20
    #39807327
hVostt
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
CompositumТ.е. эти типы известны по обе стороны.

Известен интерфейс на уровне ссылке на общую сборку.
...
Рейтинг: 0 / 0
29.04.2019, 07:26
    #39807403
Сон Веры Павловны
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Хм. FW 4.7.1, всё в точности так, как описано выше, только для чистоты эксперимента интерфейс ICommand вынесен еще в одну сборку, и главный проект имеет референс только на неё, на сборку с классом атрибута у главного проекта референса нет:
Консольный вывод:

Код: plaintext
1.
2.
3.
4.
5.
6.
Current domain: test3.exe
Domain: AD #2
command type: AntPluginExample1.HelloWorld
Attributes:
        SerializableAttribute
        CommandIdAttribute
Command disposed
hVostt
Код: c#
1.
2.
3.
4.
5.
6.
    public interface ICommand
    {
        public void Dispose();
        public void Execute();
        public Guid GetCommandId();
    }


Попробуйте, я не проверял :)
Это видно :)
...
Рейтинг: 0 / 0
29.04.2019, 22:41
    #39808014
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Сон Веры Павловны
Код: plaintext
1.
SerializableAttribute
        CommandIdAttribute

Ого... Подтверждаю. Странно... Вот уж не ожидал такого сюрприза.
...
Рейтинг: 0 / 0
29.04.2019, 22:42
    #39808015
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Сон Веры ПавловныХм. FW 4.7.1
В 4.7.2 тоже всё норм. Проблема только в 4.8.
...
Рейтинг: 0 / 0
29.04.2019, 23:05
    #39808024
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Compositum,

На этом странности не закончились: переключился обратно на .Net 4.8 и... Всё работает! Я не знаю что это было.
...
Рейтинг: 0 / 0
30.04.2019, 06:36
    #39808078
Сон Веры Павловны
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
CompositumЯ не знаю что это было.
Можно попробовать воспроизвести начальную ситуацию (когда искомого атрибута нет), и распотрошить сборку плагина ildasm'ом.
У меня это выглядит так:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
.class public auto ansi sealed serializable beforefieldinit AntPluginExample1.HelloWorld
    extends [mscorlib]System.MarshalByRefObject
    implements [Ant.Interfaces]Ant.Interfaces.ICommand,
               [mscorlib]System.IDisposable
{
    .custom instance void [Ant.Core]Ant.Core.CommandIdAttribute::.ctor(string) = (
        01 00 26 7b 33 38 32 46 35 41 31 34 2d 39 41 39
        36 2d 34 41 30 35 2d 38 30 42 33 2d 31 30 46 45
        31 37 30 42 35 39 44 45 7d 00 00
    )
    ................
}


и в метаданных сборки

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
TypeRef #16 (01000010)
-------------------------------------------------------
Token:             0x01000010
ResolutionScope:   0x23000002
TypeRefName:       Ant.Core.CommandIdAttribute
	MemberRef #1 (0a00000f)
	-------------------------------------------------------
		Member: (0a00000f) .ctor: 
		CallCnvntn: [DEFAULT]
		hasThis 
		ReturnType: Void
		1 Arguments
			Argument #1:  String

- т.е. всё на своих местах, и этого атрибута просто не может не быть. Возможно, в изначально описываемом случае это могло выглядеть как-то иначе.
...
Рейтинг: 0 / 0
30.04.2019, 09:07
    #39808141
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Сон Веры Павловны,

Я уверен в том, что это я где-то накосячил и затем исправил (сам не заметив как). :) Правда я так и не понял в чём был этот самый "косяк"... Ну ок, главное - это то, что теперь всё работает.

Вы не в курсе, какой механизм в .NET Core пришёл на смену AppDomain?

В .Net Framework я загружаю каждый плагин в отдельный AppDomain. Это позволяет мне управлять настройками безопасности для каждого плагина индивидуально (если это понадобится), а так же выгружать плагин, если он начтёт работать криво, без необходимости перезагрузки приложения в целом. Хотелось бы такой подход применять и в .Net Core, но, насколько сейчас помню, я получал сообщение о том, что не может быть более одного AppDomain...

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
using System;
using System.Threading;
using Ant.Core;
using System.Linq;
using static System.Console;
using System.Reflection;

namespace Ant.CLI {
    class Program {
        static void Main(string[] args) {
            Title = "Ant CLI";
            var currentMethodName = MethodBase.GetCurrentMethod().Name;

            WriteLine($"Method: {currentMethodName} is working in the " +
                $"'{Thread.GetDomain().FriendlyName}' domain.");

            var domain = AppDomain.CreateDomain("AppDomain #2", null, null);
            var asm = domain.Load("AntPluginExample1, version=1.0.0, " +
                "culture=neutral, PublickKeyTocken=null");
            var cmdInterfaceName = "Ant.Core.ICommand";

            foreach (var tc in asm.ExportedTypes.Where(
                n => n.IsClass && !n.IsAbstract &&
                null != n.GetInterface(cmdInterfaceName))) {
                using (var command = (ICommand)domain.CreateInstanceAndUnwrap(
                        asm.FullName, tc.FullName)) {
                    var t = command.GetType();
                    var cmdAttrib = Attribute.GetCustomAttribute(t,
                        typeof(CommandAttribute)) as CommandAttribute;
                    WriteLine($"Class '{t}':");
                    WriteLine($"\tCommand id: {cmdAttrib.Id}");
                    WriteLine($"\tCommand logical name: {cmdAttrib.LogicalName}");
                    command.Execute();
                }
            }
            AppDomain.Unload(domain);
            WriteLine("Press ENTER for exit...");
            ReadKey();
        }
    }
}
...
Рейтинг: 0 / 0
30.04.2019, 09:53
    #39808159
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
CompositumВы не в курсе, какой механизм в .NET Core пришёл на смену AppDomain?
Ответ нашёл у Майкрософта здесь .
...
Рейтинг: 0 / 0
30.04.2019, 09:56
    #39808161
hVostt
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Compositum,

Не пробовали поработать c MEF, если хочется загружать сборки динамически?

Всё же, несмотря на то, что атрибут таки получить можно, от себя рекомендовал бы работать по контрактам. Хотите, например, получать каталог команд (Type, Id), лучше это делать через выделенный провайдер.
...
Рейтинг: 0 / 0
30.04.2019, 09:59
    #39808164
Petro123
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
Compositum,
Core это кроссплатформенное приложение. Вроде под десктоп MS еще ничего не сообразил как будет. Поэтому у нас пишут на С++ и Qt.
Про безопасность, то в линуксе она решается другими мерами. Например сертифицированная Ось астра производстра РФ и мандатная система безопасности.
...
Рейтинг: 0 / 0
30.04.2019, 10:05
    #39808169
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
hVosttНе пробовали поработать c MEF, если хочется загружать сборки динамически?
Не пробовал. :)

hVosttВсё же, несмотря на то, что атрибут таки получить можно, от себя рекомендовал бы работать по контрактам. Хотите, например, получать каталог команд (Type, Id), лучше это делать через выделенный провайдер.
Можете пояснить? У меня слово "контракты" ассоциируется либо как синоним слова "интерфейс", либо с WCF.
...
Рейтинг: 0 / 0
30.04.2019, 10:05
    #39808170
fkthat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
CompositumВы не в курсе, какой механизм в .NET Core пришёл на смену AppDomain?


AssemblyLoadContext. Я уже даже делал простенькую плагинную систему для демо с ним. Каждая сборка-плагин грузится в свою "песочницу" и может иметь свои собственные зависимости, не конфликтуя с другими. Вроде бы в третей коре обещали все это даже упростить.
...
Рейтинг: 0 / 0
30.04.2019, 10:16
    #39808179
Petro123
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
hVosttMEF
+1
[Exprort("Shell", typef(ConainerControl))]
...
Рейтинг: 0 / 0
30.04.2019, 10:19
    #39808180
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
fkthatAssemblyLoadContext. Я уже даже делал простенькую плагинную систему для демо с ним. Каждая сборка-плагин грузится в свою "песочницу" и может иметь свои собственные зависимости, не конфликтуя с другими. Вроде бы в третей коре обещали все это даже упростить.

Да, я уже читал про AssemblyLoadContext:
MicrosoftСоздание дополнительных доменов приложений не поддерживается. И мы не планируем добавлять эту возможность в будущем. Вместо нее для изоляции кода мы рекомендуем использовать отдельные процессы или контейнеры. Для динамической загрузки сборок рекомендуется использовать новый класс AssemblyLoadContext.

В материале, касательно AssemblyLoadContext вижу такой фрагмент текста:
MicrosoftAssemblyLoadContext Представляет контекст загрузки. По существу контекст загрузки создает область для загрузки, разрешение и потенциально выгрузки набор сборок .

Не совсем понял слово "потенциально" в данном контексте. Да и "набор сборок" - тоже не совсем понятен. В .Net Framework сборку выгрузить нельзя - можно выгружать только домен целиком. Т.е. складывается впечатление, что на .Net Core можно выгружать отдельно сборку (если посмотреть на неё как на набор, состоящий из одного элемента).
...
Рейтинг: 0 / 0
30.04.2019, 10:34
    #39808190
fkthat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
CompositumНе совсем понял слово "потенциально" в данном контексте. Да и "набор сборок" - тоже не совсем понятен. В .Net Framework сборку выгрузить нельзя - можно выгружать только домен целиком. Т.е. складывается впечатление, что на .Net Core можно выгружать отдельно сборку (если посмотреть на неё как на набор, состоящий из одного элемента).

Не знаю, у них сейчас все что связано с этим как-то почти что не задокументировано. Приходилось гуглить по всяким блогам. У меня было как-то так:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
    public class PluginLoadContext : AssemblyLoadContext
    {
        private readonly string _dir;

        public PluginLoadContext(string dir)
        {
            _dir = dir ?? throw new ArgumentNullException(nameof(dir));
        }

        protected override Assembly Load(AssemblyName assemblyName)
        {
            var path = Path.Combine(_dir, assemblyName.Name) + ".dll";
            return File.Exists(path) ? LoadFromAssemblyPath(path) : null;
        }
    }
}


Затем:
Код: c#
1.
2.
3.
4.
var loadContext = new PluginLoadContext(plugin.FolderPath);
var assembly = loadContext.LoadFromAssemblyName(new AssemblyName(plugin.Assembly));
var pluginType = assembly.GetType(plugin.Type);
containerBuilder.RegisterType(pluginType).Named<IPlugin>(plugin.Name);


Я не знаю, насколько это все корректно, так как документации вообще нет, но оно работало. Я даже проверял на предмет возможного конфликта зависимостей - все было ок.
...
Рейтинг: 0 / 0
30.04.2019, 10:42
    #39808195
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Возможно ли через прокси получать информацию о пользовательских атрибутах класса?
fkthat,

Благодарю за код! Заодно узнал о существовании ContainerBuilder - буду читать про него. :)
...
Рейтинг: 0 / 0
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Возможно ли через прокси получать информацию о пользовательских атрибутах класса? / 19 сообщений из 19, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]