2012-01-14 6 views
2

実行時に読み取られるメタデータからWebサービスを構築します。私はWebサービス全体を意味します:署名、契約、実装。実行時にWCFサービスを作成する

私はここから見る二つの主要なパスがあります。

最初のパスを使用すると、コードを生成することです。文字列にC#コードを生成し、それを即座にコンパイルするか、よりエレガントに(そして複雑に)コンパイルして、MSILコードを発行します。 WCFコードを持っているので、WCFはそこからWSDLを生成します。

第二の経路は、一般的なサービスを利用することです。すべてを受け入れる操作Message Process(Message)を持つサービス。私たちはまだこのサービスを「通常の」サービスとして公開したいので、どこかで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は次のとおりです。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); 
} 

} メタデータとともに動作を定義するため、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; } 
} 

複雑なタイプの場合は、これがあなたの質問のルートである判定コールを行う必要があります。タイプの発行方法です。ここに例がありますが、あなたはそれが見えるようにすることができます。

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提供されています:私は(それがデモコードです警告は)私のServiceHostFactoryでこれを呼んでいます。

+0

実例がありますか? – Beats

+0

優秀、ありがとう – Beats

関連する問題