使用SelfHosting时,我可以自动托管app.config中的所有服务吗?

我正在编写一个需要托管多个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.

我想在内部解析app.config文件时,WCF会尝试匹配服务名称的文字字符串。

编辑/答案 :我最终做的基本上是下面的答案。 而不是使用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); 

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

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

MyNamespace.MyService.MyServiceImpl

假设程序集是:

MyNamespace.MyService

  // get the  /  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  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中搜索所有程序集中的服务

那应该是可能的! 查看此代码片段 – 将其用作基础并从此处开始:

 using System.Configuration; // don't forget to add a reference to this assembly! // get the  /  config section ServicesSection services = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection; // enumerate over each  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 eg 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 (eg the list of types you need to find from // parsing the app.config file) foreach(Type ty in types) { // do something here } } }