2012-01-14 76 views
2

我们将从运行时读取的元数据构建Web服务。我的意思是整个Web服务:签名,合同和实现。在运行时创建WCF服务

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

第一条路径是您生成代码。要么你在字符串中生成C#代码,并且在飞行中编译它,或者更优雅地(并且复杂地)编译它,你会发出MSIL代码。这样你有WCF代码,WCF将负责从中生成WSDL。

第二种方法是使用通用服务。具有操作消息处理(消息)接受一切的服务。我们仍然希望将服务作为“正常”服务公开,所以我需要某个地方的WSDL。我如何创建一个WSDL?我想过使用System.ServiceModel.Description直到我意识到内部深处,这个API取决于具体的类型。通过这种方法,我们不会有任何数据合同类型,并且可以即时处理XML,并使用元数据来解释它。所以我们需要以某种方式生成WSDL。这是一个疯狂的想法? WSDL有一个相当复杂的规范...

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

建议?

回答

3

这是一个痛苦的事情,但可能会发射类型。创建您的.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

+0

你有这样的工作例子吗? – Beats 2014-02-20 14:23:33

+0

非常好,非常感谢 – Beats 2014-02-24 15:08:28