2012-01-14 94 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