在运行时创建 WCF 服务 [英] Creating WCF Service at runtime

查看:25
本文介绍了在运行时创建 WCF 服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We are going to build a web service from metadata read at runtime. I mean the entire web service: the signatures, contracts and implementation.

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

There are two main paths I see from here.

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

The first path is that you generate code. Either you generate C# code in strings and compile it on the fly or more elegantly (and complicatedly), you emit MSIL code. This way you have WCF code and WCF will take care of generating the WSDL from it.

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

The second path is to use a generic service. A service with an operation Message Process(Message) accepting everything. We still want to expose the service as a 'normal' service, so I would need a WSDL somewhere. How can I create a WSDL? I thought about using System.ServiceModel.Description until I realised that deep inside, this API depends on concrete types. With this approach we wouldn't have any data contract type and would process XML on the fly, using metadata to interpret it. So we need somehow to generate the WSDL. Is that a crazy idea? WSDL has quite a complicated spec...

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

A third option would be to use an hybrid approach, emitting types just to create signatures but implementing the service using non-emitted code (reflecting on emitted types). Weird, but might be simpler than hand crafting WSDL by hand...

建议?

推荐答案

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

It's a pain to do, but possible to emitting types. Create your .svc and point it at your custom ServiceHostFactory:

<%@ ServiceHost Language="C#" Debug="true" Factory="Foo.FooServiceHostFactory" %>

[ServiceContract]
public class FooService { }

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

Your ServiceHostFactory is where you generate your operation contracts, types that go with it, etc:

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(或者您可以单独实现它们),如下所示:

} In the ServiceHostFactory you define the behaviors along with the metadata, so your behavior would need to implement IOperationBehavior and IOperationInvoker (or you could implement them separately) and look something like this:

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 中调用它(警告:它是演示代码)

For complex types, this is where you need to make a judgement call, which is the root of your question, is how to emit the types. Here's an example, but you can do it any way you can see fit. I'm calling this in my ServiceHostFactory (warning: it's demo code)

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);
    }
}

更新:我写了一个简单的例子,它在这里可用.

update: I wrote a quick example and it is available here.

这篇关于在运行时创建 WCF 服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆