Creating WCF Service at runtime

此生再无相见时 提交于 2019-12-03 21:57:02

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 { }

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

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

}

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!