2012-01-14 1 views
2

Мы собираемся создать веб-службу из метаданных, прочитанных во время выполнения. Я имею в виду всю веб-службу: подписи, контракты и реализацию.Создание службы 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 является, где вы создаете свои операции контракты, типы, которые идут с ним, и т.д.:

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

+0

Отлично, спасибо большое – Beats

 Смежные вопросы

  • Нет связанных вопросов^_^