2012-01-14 3 views
2

런타임에 읽는 메타 데이터에서 웹 서비스를 빌드하려고합니다. 전체 웹 서비스 : 서명, 계약 및 구현을 의미합니다.런타임에서 WCF 서비스 만들기

여기에서 볼 수있는 두 가지 주요 경로가 있습니다.

첫 번째 경로는 코드를 생성하는 것입니다. 문자열에서 C# 코드를 생성하고 컴파일하거나보다 우아하게 (그리고 복잡하게) 컴파일하면 MSIL 코드가 방출됩니다. 이렇게하면 WCF 코드가 생기고 WCF는 WSDL을 생성합니다.

두 번째 경로는 일반 서비스를 사용하는 것입니다. 모든 작업을 수락하는 Message Process (Message) 작업이있는 서비스입니다. 우리는 여전히 서비스를 '정상적인'서비스로 공개하고자하므로 어딘가에서 WSDL이 필요합니다. WSDL은 어떻게 만들 수 있습니까? 그 내부 깊숙이 깨달을 때까지 System.ServiceModel.Description을 사용하려고 생각했습니다.이 API는 구체적인 유형에 달려 있습니다. 이 접근 방식을 사용하면 데이터 계약 유형이 없으므로 XML을 해석 할 때 메타 데이터를 사용하여 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; } 
} 

복잡한 유형의 경우 여기에서 질문의 루트 인 판단 호를해야합니다. 유형을 내보내는 방법입니다. 여기에 예제가 있지만, 당신이보기에 어떤 방식 으로든 그것을 할 수 있습니다. 내 ServiceHostFactory이 전화 해요 내가 빠른 예를 썼다는 here로 볼 수 있습니다 : :

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

업데이트 (경고는 데모 코드이다).

+0

실례가 있습니까? – Beats

+0

훌륭합니다, 대단히 감사합니다. – Beats