sweethome

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

WHY

Q: "啥? 一个ServiceHost只能寄宿一个ServiceType?? 那我有好多业务逻辑难道要我都塞到一个类里吗?"

A: 哦,你可以把业务逻辑抽像到多个Interface,然后用一个类来实现

Q: "那我的项目可能有多个人参与,每个人负责某一个模块,难道要我们N个人去维护一个cs类文件吗?"

A:你不知道有一个东西叫partial class吗?

Q:  "shit,你是说每回我要往服务端添加一些业务功能,都得重新编译一遍程序吗?"

A: 呃...这个...也许你可以...like this

http://www.cnblogs.com/levinknight/archive/2007/05/25/760176.html

and this

http://www.cnblogs.com/jillzhang/archive/2008/11/02/1325081.html

Q: “oh no, 这样我的系统还叫系统吗? 针对每个service一遍又一遍的权限验证和安全配置?”

A: >.<

WANT

1, 以插件的方式开发wcf服务端,业务模块以独立dll的方式注册到宿主里

2,业务模块的添加删除是动态的,服务端主程序无需重新编译

3,无须为每个业务模快servicetype单独做配置,注册一个业务模块只需在host里添加一个endpoint和操作契约

...

HOW

默认情况下,servicehost在收到客户端调用时,可以判断出调用的契约interface, 然后用构造函数传入的servicetype创建实例并转型为对应的interface进行方法调用

而为了实现一个servicehost能对应多个服务, 我们需要改变他的实例创建的行为,WCF里可以用InstanceProvider来实现这一机制.

具体InstanceProvider用法及wcf服务端的消息dispatch机制可以参考园子里的大牛Artech的《WCF后续之旅》系列文章:

http://www.cnblogs.com/artech/archive/2008/08/26/1276559.html

public class MyInstanceProvicer : IInstanceProvider
{
    private string contractType;
    public MyInstanceProvicer(string contractType)
    {
        this.contractType = contractType;
    }
    public object GetInstance(InstanceContext instanceContext, 
        System.ServiceModel.Channels.Message message)
    {
        /*在注册业务模块的时候,可以把实现业务模块的assembly,业务模块的type name,
         * 业务模块所实现的契约接口记录到一张表里,然后在这里通过契约找到对应的业
         * 务模块信息,实例化出类实例,返回给servicehost*/
        switch (contractType)
        {
            case "IFoobar":
                return new WcfService1.Service1();
                break;
            case "ISayHello":
                return new WcfService2.Service1();
                break;
            default:
                return null;
                break;
        }
        return null;
    }
    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }
    public void ReleaseInstance(InstanceContext instanceContext, 
        object instance)
    {
        Console.WriteLine("releaseInstance::" + instance.ToString());
    }
}
public class InstanceProviderBehavior : IServiceBehavior
{
    void IServiceBehavior.AddBindingParameters(
        ServiceDescription serviceDescription, 
        ServiceHostBase serviceHostBase, 
        System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, 
        System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }
    void IServiceBehavior.ApplyDispatchBehavior(
        ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher channelDispatcher 
            in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher endpointDispatcher 
                in channelDispatcher.Endpoints)
            {
                endpointDispatcher.DispatchRuntime.InstanceProvider = 
                    new MyInstanceProvicer(endpointDispatcher.ContractName);
            }
        }
    }
    void IServiceBehavior.Validate(ServiceDescription serviceDescription, 
        ServiceHostBase serviceHostBase)
    {
    }
}

通过ServiceBehavior把InstanceProvider应用到servicehost上

host.Description.Behaviors.Add(new InstanceProviderBehavior());

通过以上的方法,事实上我们已经拦截了servicehost对servicetype的实例化过程,可是我们还有一个问题得解决,这个servicehost本身应该怎么实例化呢?构造函数里的servicetype传什么??

由于servicehost在初始化的时候,会检查传入构造函数的serivcetype是否实现了在endpoint里注册的所有契约interface,只要有一个interface没实现就会抛出异常.

这可怎么办?我可不知道最后我的serivcehost会host多少个契约,这个servicetype是没办法在开发期间实现的.

幸好,dotnet提供了反射和动态创建类型的功能,在实例化servicehost的时候,把所有注册的业务模块契约接口列出来,并动态创建一个实现了这些接口的类型就可以了.

参考:

通过Emit实现动态类生成

http://www.souzz.net/html/edu/net/net11/3478.html

public class TypeCreator
{
    public TypeCreator()
    {
    }
    public Type build()
    {
        AppDomain currentAppDomain = AppDomain.CurrentDomain;
        AssemblyName assyName = new AssemblyName();
        assyName.Name = "DynamicAssemblyForServiceType";
        AssemblyBuilder assyBuilder = 
            currentAppDomain.DefineDynamicAssembly(assyName, 
            AssemblyBuilderAccess.Run);
        ModuleBuilder modBuilder = 
            assyBuilder.DefineDynamicModule("DynamicModuleForServiceType");
        String newTypeName = "DynamicServiceType";
        TypeAttributes newTypeAttribute = 
            TypeAttributes.Class | TypeAttributes.Public;
        //在这里把注册的业务模块契约列出来
        Type[] newTypeInterfaces = new Type[] {
            typeof(WcfService1.IFoobar), typeof(WcfService2.ISayHello) };
        TypeBuilder typeBuilder = modBuilder.DefineType(newTypeName, 
            newTypeAttribute, null, newTypeInterfaces);
        //实现接口
        foreach (Type baseInterface in newTypeInterfaces)
        {
            MethodInfo[] targetMethods = baseInterface.GetMethods();
            foreach (MethodInfo targetMethod in targetMethods)
            {
                if (targetMethod.IsVirtual)
                {
                    ParameterInfo[] paramInfo = targetMethod.GetParameters();
                    Type[] paramType = new Type[paramInfo.Length];
                    for (int i = 0; i < paramInfo.Length; i++)
                        paramType[i] = paramInfo[i].ParameterType;
                    MethodBuilder methodBuilder = typeBuilder.DefineMethod(
                        targetMethod.Name, 
                        MethodAttributes.Public | MethodAttributes.Virtual, 
                        targetMethod.ReturnType, paramType);
                    ILGenerator ilGen = methodBuilder.GetILGenerator();
                    ilGen.Emit(OpCodes.Ret);   //该方法具体应该实现什么功能不重要.
                }
            }
        }
        return (typeBuilder.CreateType());
    }
}

现在有了servicetype,可以创建servicehost的实例了

ServiceHost host = new ServiceHost(new TypeCreator().build());
host.Description.Behaviors.Add(new InstanceProviderBehavior());
host.Open();

END

以上方法,基本实现了一个ServiceHost多个服务的功能,但是还没深入评估,园子里牛人多,希望能指出其中可能存在的陷阱、错误和不足, 拍谢!

posted on 2010-11-16 16:24  kingcomxu  阅读(5576)  评论(8编辑  收藏  举报