7

我正在编写一个需要托管多个 WCF 服务的应用程序。WCF 的优势之一是能够通过在 app.config 文件中指定设置来配置服务而无需重新编译。

自托管时,似乎没有一种开箱即用的方式来自动托管 app.config 文件中的服务。我发现这个问题提到了一个可能的解决方案,即在运行时动态枚举 app.config 中列出的服务,并为每个服务创建一个 ServiceHost。

但是,我的服务、合同和托管应用程序都在不同的程序集中。这会导致Type.GetType(string name)无法找到我的服务类型(返回null),因为它是在不同的程序集中定义的。

如何可靠地动态托管 app.config 文件中列出的所有服务(即,new ServiceHost(typeof(MyService))在我的自托管应用程序中不进行硬编码?

注意:我的 app.config 是使用 Visual Studio 2010 中的“WCF 配置编辑器”生成的。

另请注意:我的主要目标是让 app.config 文件驱动它,这样就有了单点配置。我不想在单独的位置配置它。

编辑:我能够读取 app.config 文件(请参见此处),但需要能够解析不同程序集中的类型。

编辑:下面的答案之一提示我尝试在 app.config 中指定 AssemblyQualifiedName 而不仅仅是基本类型名称。这能够解决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);
4

3 回答 3

5
    // 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 中搜索所有程序集以查找服务

于 2012-10-21T16:25:35.603 回答
4

这绝对应该是可能的!查看此代码片段 - 将其用作基础并从这里开始:

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
      }
   }
}
于 2011-05-13T15:11:33.793 回答
1

您在问题链接中正确确定了问题的答案,@marc_s 的答案也给出了正确的方法。您遇到的实际问题是您需要动态获取只能通过配置文件引用的程序集的 Type 实例,因此它可能不会加载到当前的 AppDomain 中。

查看这篇博文,了解在代码中动态引用程序集的方法。尽管这篇文章专门针对 ASP.NET 应用程序,但一般方法应该适用于自托管方案。想法是将 Type.GetType(string) 调用替换为动态加载请求的程序集(如果需要)并返回 Type 对象的私有方法调用。您发送此方法的参数仍然是 element.Name,您需要确定要加载的正确程序集。一个简单的基于约定的程序集命名方案应该可以工作。例如,如果服务类型是:

MyNamespace.MyService.MyServiceImpl

然后假设程序集是:

我的命名空间.MyService

于 2011-05-13T16:20:17.510 回答