我的目标是创建一个能够解析多个程序集、检测契约(Contract)和托管服务的宿主应用程序。
为了加载服务,我们通常需要对 servicehost 实例化进行硬编码。尽管不是我正在寻找的行为,但以下代码仍在工作。
ServiceHost wService1Host = new ServiceHost(typeof(Service1));
wService1Host.Open();
ServiceHost wService2Host = new ServiceHost(typeof(Service2));
wService2Host.Open();
然而,这意味着我提前知道服务是什么。
我不介意引用包含服务的程序集。我只是想让主机不知道程序集中包含哪些服务。例如,如果我向其中一个程序集添加新服务,则主机端不需要任何更改。
这与 question 非常相似,但由于上述原因增加了复杂性。
这是我到目前为止提供的主机代码。我现在不介意管理服务,我只是希望它们被正确加载。
class Program
{
static void Main(string[] args)
{
// 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);
// I've hardcoded the name of the assembly containing the services only to ease debugging
if (assemblyToInspect != null && assemblyToInspect.GetName().Name == "WcfServices")
{
// 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)
{
Assembly implementationAssembly = Assembly.GetAssembly(ty);
// When loading the type for the service, load it from the implementing assembly.
Type implementation = implementationAssembly.GetType(ty.FullName);
ServiceHost wServiceHost = new ServiceHost(implementation); // FAIL
wServiceHost.Open();
}
}
}
Console.WriteLine("Service are up and running.");
Console.WriteLine("Press <Enter> to stop services...");
Console.ReadLine();
}
}
尝试创建 serviceHost 时出现以下错误:
"It is illegal to reflect on the custom attributes of a Type loaded via ReflectionOnlyGetType (see Assembly.ReflectionOnly) -- use CustomAttributeData instead."
在上面给出的链接中,这个人似乎已经使用 typeof 解决了它的问题,因为他事先知道他想要公开什么服务。不幸的是,这不是我的情况。
注意:对于托管部分,我实际上有 3 个项目。第一个是宿主应用程序(见上文),第二个是包含我所有服务契约(接口(interface))的程序集,最后一个程序集包含服务实现。
这是我实际用于托管服务的 app.config。包含实现的程序集命名为“WcfServices”并包含 2 个服务。一种是公开回调,另一种是公开基本服务。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WcfServices.Service1"
behaviorConfiguration="metadataBehavior">
<endpoint address="Service1Service"
binding="basicHttpBinding"
contract="WcfServices.IService1"
name="basicHttp"/>
<endpoint binding="mexHttpBinding"
contract="IMetadataExchange"
name="metadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/Service1"/>
</baseAddresses>
</host>
</service>
<service name="WcfServices.Service2"
behaviorConfiguration="metadataBehavior">
<endpoint address="Service2Service"
binding="wsDualHttpBinding"
contract="WcfServices.IService2"/>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/Service2"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
所以,要清楚,这就是我要找的:
1.在当前应用程序目录中加载程序集
2.看里面有没有契约实现
3. 如果有,实例化这些服务(暂时使用 app.config)
首先,这甚至可能吗? (我的猜测是因为名为 wcfstorm 的应用程序似乎已经这样做了)
显然,我怎样才能使上面的代码有效?
谢谢!
最佳答案
这是我最终做的:
private static void LoadServices()
{
// find currently executing assembly
Assembly Wcurr = Assembly.GetExecutingAssembly();
// get the directory where this app is running in
string wCurrentLocation = Path.GetDirectoryName(Wcurr.Location);
// enumerate over those assemblies
foreach (string wAssemblyName in mAssemblies)
{
// load assembly just for inspection
Assembly wAssemblyToInspect = null;
try
{
wAssemblyToInspect = Assembly.LoadFrom(wCurrentLocation + "\\" + wAssemblyName);
}
catch (System.Exception ex)
{
Console.WriteLine("Unable to load assembly : {0}", wAssemblyName);
}
if (wAssemblyToInspect != null)
{
// find all types with the HostService attribute
IEnumerable<Type> wTypes = wAssemblyToInspect.GetTypes().Where(t => Attribute.IsDefined(t, typeof(HostService), false));
foreach (Type wType in wTypes)
{
ServiceHost wServiceHost = new ServiceHost(wType);
wServiceHost.Open();
mServices.Add(wServiceHost);
Console.WriteLine("New Service Hosted : {0}", wType.Name);
}
}
}
Console.WriteLine("Services are up and running.");
}
注意:此方法要求“宿主”项目引用程序集。
注2:为了加速程序集解析,我硬编码了要在“mAssemblies”中加载的程序集。
关于c# - 使用反射的 Wcf 动态托管,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13034546/