我正在编写一个需要托管多个WCF服务的应用程序。 WCF的优势之一是能够通过在app.config文件中指定设置来配置服务而无需重新编译。
当自托管时,似乎没有一种开箱即用的方式来自动托管app.config文件中的服务。我找到了 this question ,其中提到了在运行时动态枚举app.config中列出的服务的可能解决方案,并为每个服务创建了一个ServiceHost。
但是,我的服务,合同和托管应用程序都在不同的程序集中。这会导致Type.GetType(string name)
无法找到我的服务类型(返回null
),因为它是在不同的程序集中定义的。
如何动态地可靠地托管app.config文件中列出的所有服务(即,在我的自托管应用程序中没有硬编码new ServiceHost(typeof(MyService))
?
注意:我的app.config是使用Visual Studio 2010中的“WCF配置编辑器”生成的。
另请注意:我的主要目标是由app.config文件驱动,因此只需一个配置点。我不想在一个单独的位置配置它。
编辑:我可以阅读app.config文件(请参阅 here ),但需要能够解析不同程序集中的类型。
编辑:下面的答案之一促使我尝试在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);
答案 0 :(得分: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中搜索所有程序集以查找服务
答案 1 :(得分: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
}
}
}
答案 2 :(得分:1)
您在问题链接中正确识别了问题的答案,而@marc_s答案也提供了正确的方法。您遇到的实际问题是您需要动态获取程序集的Type实例,该程序集可能只能通过配置文件引用,因此可能无法加载到当前的AppDomain中。
在代码中查看此blog post for a way to dynamically reference assemblies。虽然该帖子专门用于ASP.NET应用程序,但一般方法应该在自托管方案中工作。这些想法是使用私有方法调用替换Type.GetType(string)调用,该调用动态加载所请求的程序集(如果需要)并返回Type对象。您发送此方法的参数仍然是element.Name,您需要确定哪个是要加载的正确程序集。一个简单的基于约定的程序集命名方案应该工作。例如,如果服务类型为:
MyNamespace.MyService.MyServiceImpl
然后假设程序集是:
MyNamespace.MyService