Могу ли я автоматически размещать все службы в app.config при использовании SelfHosting? - PullRequest
7 голосов
/ 13 мая 2011

Я пишу приложение, которое должно содержать несколько служб WCF.Одной из сильных сторон WCF является возможность настраивать службы без перекомпиляции, указав параметры в файле app.config.

При самостоятельном размещении, по-видимому, не существует-box способ автоматического размещения служб, которые находятся в файле app.config.Я нашел этот вопрос , в котором упоминается возможное решение динамического перечисления служб, перечисленных в app.config во время выполнения, и создания ServiceHost для каждого.

Однако мои службы, контракты и приложение хостинга находятся в разных сборках.Это приводит к тому, что Type.GetType(string name) не может найти тип моей службы (возвращает null), поскольку он определен в другой сборке.

Как надежно разместить все службы, перечисленные в файле app.configдинамически (т.е. без жесткого кодирования new ServiceHost(typeof(MyService)) в моем приложении для самостоятельного размещения?

Примечание. Мой app.config был создан с помощью «Редактора конфигурации WCF» в Visual Studio 2010.

Примечание также: моя основная цель - чтобы это управлялось файлом app.config, чтобы была единственная точка конфигурации. Я не хочу настраивать это в отдельном месте.

EDIT : я могу прочитать файл app.config (см. здесь ), но мне нужно разрешить типы в разных сборках.

EDIT : Один из приведенных ниже ответов побудил меня попробовать указать AssemblyQualifiedName в app.config вместо простого имени основного типа. Однако это позволило обойти проблему Type.GetType(), однако ServiceHost.Open() теперь терпит неудачу с InvalidOperationExceptionнезависимо от того, как я получаю тип:

// Fails
string typeName = typeof(MyService).AssemblyQualifiedName;
Type myType = Type.GetType(typeName);
ServiceHost host = new ServiceHost(myType);
host.Open(); // throws InvalidOperationException

// Also fails
Type myType2 = typeof(MyService);
ServiceHost host2 = new ServiceHost(myType2);
host2.Open(); // throws InvalidOperationException

Сведения об исключении:

Service 'SO.Example.MyService' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.

Я полагаю, WCF пытается сопоставить литеральную строку для имени службы при разборефайл app.config внутри.

РЕДАКТИРОВАТЬ / ОТВЕТИТЬ : То, что я закончил, было в основном тем, что было в ответе ниже.Вместо использования Type.GetType() я знаю, что все мои сервисы находятся в одной сборке, поэтому я переключился на:

// Get a reference to the assembly which contain all of the service implementations.
Assembly implementationAssembly = Assembly.GetAssembly(typeof(MyService));
...
// When loading the type for the service, load it from the implementing assembly.
Type implementation = implementationAssembly.GetType(serviceElement.Name);

Ответы [ 3 ]

5 голосов
/ 21 октября 2012
    // get the <system.serviceModel> / <services> config section
    ServicesSection services = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;

    // get all classs
    var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(s => s.GetTypes()).Where(t => t.IsClass == true);

    // enumerate over each <service> node
    foreach (ServiceElement service in services.Services)
    {
        Type serviceType = allTypes.SingleOrDefault(t => t.FullName == service.Name);
        if (serviceType == null)
        {
            continue;
        }

        ServiceHost serviceHost = new ServiceHost(serviceType);
        serviceHost.Open();
    }

Основываясь на других ответах, я расширил код до следующего, который ищет во всех сборках службы в app.config

4 голосов
/ 13 мая 2011

Это определенно должно быть возможно!Проверьте этот фрагмент кода - используйте его в качестве основы и перейдите отсюда:

using System.Configuration;   // don't forget to add a reference to this assembly!

// get the <system.serviceModel> / <services> config section
ServicesSection services = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;

// enumerate over each <service> node
foreach(ServiceElement aService in services.Services)
{
    Console.WriteLine();
    Console.WriteLine("Name: {0} / Behavior: {1}", aService.Name, aService.BehaviorConfiguration);

    // enumerate over all endpoints for that service
    foreach (ServiceEndpointElement see in aService.Endpoints)
    {
        Console.WriteLine("\tEndpoint: Address = {0} / Binding = {1} / Contract = {2}", see.Address, see.Binding, see.Contract);
    }
}

Это прямо сейчас просто выводит информацию - но вы могли бы определенно использовать это для фактического создания ваших сервисных хостов внутри ваших собственныхСлужба NT!

Обновление: хорошо, извините, я упустил ваш самый важный момент - факт, что службы находятся в разных сборках.

В этом случае вам нужнодля динамической загрузки этих сборок по мере необходимости - вы можете, например, «просто знать», какие сборки загружать, или вы можете поместить их все в определенные подкаталоги и загрузить все сборки в этом каталоге, или вы можете просто проверить все сборки в одном и том жеместо, где находится ваш MyOwnServiceHost.exe, и проверьте, есть ли какие-либо типы, которые вам нужны.

Эта часть - какой тип службы найти в какой сборке - не обрабатывается конфигурацией WCF - вам нужно сделать это самостоятельнолюбым способом, который имеет для вас наибольшее значение.

// find currently executing assembly
Assembly curr = Assembly.GetExecutingAssembly();

// get the directory where this app is running in
string currentLocation = Path.GetDirectoryName(curr.Location);

// find all assemblies inside that directory
string[] assemblies = Directory.GetFiles(currentLocation, "*.dll");

// enumerate over those assemblies
foreach (string assemblyName in assemblies)
{
   // load assembly just for inspection
   Assembly assemblyToInspect = Assembly.ReflectionOnlyLoadFrom(assemblyName);

   if (assemblyToInspect != null)
   {
      // find all types
      Type[] types = assemblyToInspect.GetTypes();

      // enumerate types and determine if this assembly contains any types of interest
      // you could e.g. put a "marker" interface on those (service implementation)
      // types of interest, or you could use a specific naming convention (all types
      // like "SomeThingOrAnotherService" - ending in "Service" - are your services)
      // or some kind of a lookup table (e.g. the list of types you need to find from
      // parsing the app.config file)
      foreach(Type ty in types)
      {
         // do something here
      }
   }
}
1 голос
/ 13 мая 2011

Вы правильно определили ответ на свою проблему в своей ссылке на вопрос, и ответ @marc_s также дает правильный подход. Фактическая проблема, с которой вы столкнулись, заключается в том, что вам необходимо динамически получать экземпляр Type сборки, на который можно ссылаться только через файл конфигурации, чтобы он не мог быть загружен в текущий домен приложения.

Посмотрите на это сообщение в блоге, чтобы найти способ динамически ссылаться на сборки в вашем коде. Хотя эта статья предназначена специально для приложений ASP.NET, общий подход должен работать в автономном сценарии. Идея состоит в том, чтобы заменить вызов Type.GetType (string) закрытым вызовом метода, который динамически загружает запрошенную сборку (если необходимо) и возвращает объект Type. Параметр, который вы отправляете этому методу, по-прежнему будет element.Name, и вам необходимо выяснить, какая сборка является правильной для загрузки. Должна работать простая схема именования сборок на основе соглашения. Например, если тип сервиса:

MyNamespace.MyService.MyServiceImpl

тогда предположим, что сборка:

MyNamespace.MyService

...