【问题标题】:Creating WCF Service at runtime在运行时创建 WCF 服务
【发布时间】:2012-01-14 18:18:19
【问题描述】:

我们将从运行时读取的元数据构建 Web 服务。我的意思是整个网络服务:签名、合同和实施。

我从这里看到了两条主要路径。

第一个路径是您生成代码。您可以在字符串中生成 C# 代码并即时编译它,或者更优雅(更复杂)地生成 MSIL 代码。这样您就有了 WCF 代码,WCF 将负责从中生成 WSDL。

第二条路径是使用通用服务。具有接受所有内容的操作 Message Process(Message) 的服务。我们仍然希望将服务公开为“普通”服务,因此我需要在某处使用 WSDL。如何创建 WSDL?我考虑过使用 System.ServiceModel.Description,直到我意识到在内心深处,这个 API 依赖于具体的类型。使用这种方法,我们将没有任何数据协定类型,并且会动态处理 XML,使用元数据来解释它。所以我们需要以某种方式生成 WSDL。这是一个疯狂的想法吗? WSDL 有相当复杂的规范...

第三种选择是使用混合方法,发射类型只是为了创建签名,但使用非发射代码(反映发射类型)实现服务。很奇怪,但可能比手工制作 WSDL 更简单...

建议?

【问题讨论】:

    标签: wcf wsdl reflection.emit


    【解决方案1】:

    这很痛苦,但可以发射类型。创建您的 .svc 并将其指向您的自定义 ServiceHostFactory:

    <%@ ServiceHost Language="C#" Debug="true" Factory="Foo.FooServiceHostFactory" %>
    
    [ServiceContract]
    public class FooService { }
    

    您的 ServiceHostFactory 是您生成操作合同、与之相关的类型等的地方:

    public class FooServiceHostFactory : ServiceHostFactory {
    public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses) {
        ServiceHost serviceHost = new FooServiceHost(baseAddresses);
        serviceHost.AddDefaultEndpoints();
        GenerateServiceOperations(serviceHost);
        return serviceHost;
    }
    
    private void GenerateServiceOperations(ServiceHost serviceHost) {
        var methodNames = new[] {
            new { Name = "Add" },
            new { Name = "Subtract" },
            new { Name = "Multiply" }
        };
    
        foreach (var method in methodNames) {
            foreach (var endpoint in serviceHost.Description.Endpoints) {
                var contract = endpoint.Contract;
                var operationDescription = new OperationDescription("Operation" + method.Name, contract);
                var requestMessageDescription = new MessageDescription(string.Format("{0}{1}/Operation{2}", contract.Namespace, contract.Name, method.Name), MessageDirection.Input);
                var responseMessageDescription = new MessageDescription(string.Format("{0}{1}/Operation{2}Response", contract.Namespace, contract.Name, method.Name), MessageDirection.Output);
    
                var elements = new List<FooDataItem>();
                elements.Add(new FooDataItem { Name = "X", DataType = typeof(int) });
                elements.Add(new FooDataItem { Name = "Y", DataType = typeof(int) });
    
                //note: for a complex type it gets more complicated, but the same idea using reflection during invoke()
                //object type = TypeFactory.CreateType(method.Name, elements);
                //var arrayOfType = Array.CreateInstance(type.GetType(), 0);
    
                //var parameter = new MessagePartDescription(method.Name + "Operation", contract.Namespace);
                //parameter.Type = arrayOfType.GetType();
                //parameter.Index = 0;
                //requestMessageDescription.Body.Parts.Add(parameter);
    
                var retVal = new MessagePartDescription("Result", contract.Namespace);
                retVal.Type = typeof(int);
                responseMessageDescription.Body.ReturnValue = retVal;
    
                int indexer = 0;
                foreach (var element in elements) {
                    var parameter = new MessagePartDescription(element.Name, contract.Namespace);
                    parameter.Type = element.DataType;
                    parameter.Index = indexer++;
                    requestMessageDescription.Body.Parts.Add(parameter);
                }
    
                operationDescription.Messages.Add(requestMessageDescription);
                operationDescription.Messages.Add(responseMessageDescription);
                operationDescription.Behaviors.Add(new DataContractSerializerOperationBehavior(operationDescription));
                operationDescription.Behaviors.Add(new FooOperationImplementation());
                contract.Operations.Add(operationDescription);
            }
        }
    }
    
    protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) {
        return base.CreateServiceHost(serviceType, baseAddresses);
    }
    

    } 在 ServiceHostFactory 中,您可以定义行为以及元数据,因此您的行为需要实现 IOperationBehavior 和 IOperationInvoker(或者您可以单独实现它们),看起来像这样:

    public class FooOperationImplementation : IOperationBehavior, IOperationInvoker {
    OperationDescription operationDescription;
    DispatchOperation dispatchOperation;
    
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) {
    
    }
    
    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) {
    
    }
    
    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) {
        this.operationDescription = operationDescription;
        this.dispatchOperation = dispatchOperation;
    
        dispatchOperation.Invoker = this;
    }
    
    public void Validate(OperationDescription operationDescription) {
    
    }
    
    public object[] AllocateInputs() {
        return new object[2];
    }
    
    public object Invoke(object instance, object[] inputs, out object[] outputs) {
        //this would ALL be dynamic as well depending on how you are creating your service
        //for example, you could keep metadata in the database and then look it up, etc
        outputs = new object[0];
    
        switch (operationDescription.Name) {
            case "OperationAdd":
                return (int)inputs[0] + (int)inputs[1];
            case "OperationSubtract":
                return (int)inputs[0] - (int)inputs[1];
            case "OperationMultiply":
                return (int)inputs[0] * (int)inputs[1];
            default:
                throw new NotSupportedException("wtf");
        }
    }
    
    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) {
        throw new NotImplementedException("Method is not asynchronous.");
    }
    
    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) {
        throw new NotImplementedException("Method is not asynchronous.");
    }
    
    public bool IsSynchronous {
        get { return true; }
    }
    

    }

    对于复杂类型,这是您需要做出判断的地方,这是您问题的根源,即如何发出类型。这是一个示例,但您可以以任何您认为合适的方式进行操作。我在我的 ServiceHostFactory 中调用它(警告:这是演示代码)

    static public class TypeFactory {
        static object _lock = new object();
        static AssemblyName assemblyName;
        static AssemblyBuilder assemblyBuilder;
        static ModuleBuilder module;
    
        static TypeFactory() {
            lock (_lock) {
                assemblyName = new AssemblyName();
                assemblyName.Name = "FooBarAssembly";
                assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
                module = assemblyBuilder.DefineDynamicModule("FooBarModule");
            }
        }
    
        static public object CreateType(string typeName, List<FooDataItem> elements) {
            TypeBuilder typeBuilder = module.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);
    
            foreach(var element in elements) {
                string propertyName = element.Name;
                Type dataType = element.DataType;
    
                FieldBuilder field = typeBuilder.DefineField("_" + propertyName, dataType, FieldAttributes.Private);
                PropertyBuilder property =
                    typeBuilder.DefineProperty(propertyName,
                                        PropertyAttributes.None,
                                        dataType,
                                        new Type[] { dataType });
    
                MethodAttributes GetSetAttr =
                        MethodAttributes.Public |
                        MethodAttributes.HideBySig;
    
                MethodBuilder currGetPropMthdBldr =
                    typeBuilder.DefineMethod("get_value",
                                                GetSetAttr,
                                                dataType,
                                                Type.EmptyTypes);
    
                ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
                currGetIL.Emit(OpCodes.Ldarg_0);
                currGetIL.Emit(OpCodes.Ldfld, field);
                currGetIL.Emit(OpCodes.Ret);
    
                MethodBuilder currSetPropMthdBldr =
                    typeBuilder.DefineMethod("set_value",
                                                GetSetAttr,
                                                null,
                                                new Type[] { dataType });
    
                ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
                currSetIL.Emit(OpCodes.Ldarg_0);
                currSetIL.Emit(OpCodes.Ldarg_1);
                currSetIL.Emit(OpCodes.Stfld, field);
                currSetIL.Emit(OpCodes.Ret);
    
                property.SetGetMethod(currGetPropMthdBldr);
                property.SetSetMethod(currSetPropMthdBldr);
            }
    
            Type generetedType = typeBuilder.CreateType();
            return Activator.CreateInstance(generetedType);
        }
    }
    

    更新:我写了一个简单的例子,它是可用的here

    【讨论】:

    • 太好了,非常感谢
    • 链接已不存在
    • 我找到了旧的 zip 文件,将其添加到谷歌驱动器并更新了链接。 HTH
    猜你喜欢
    • 2012-11-24
    • 1970-01-01
    • 2011-08-20
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多