Как установить ServiceHostingEnvironment.AspNetCompatibilityEnabled = true в коде (не в config).NET/С#
У меня есть требование получить доступ к HttpContext.Current из службы RESTful WCF. Я знаю, что могу добиться этого, добавив следующее в config:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
и используя следующий атрибут в моей службе:
[AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Required)]
Вот моя проблема, мне нужно "развернуть" экземпляр службы в коде для модульного тестирования, и поэтому я не могу использовать файлы конфигурации для указания bebaviours службы и т.д.
В настоящий момент мой код выглядит следующим образом, но, несмотря на очистку сети, мне не удалось выяснить, как я настроил класс ServiceHostingEnvironment и установить свойство AspNetCompatibilityEnabled в true без использования config, может ли кто-нибудь помочь?
string serviceUrl = "http://localhost:8082/MyService.svc";
_host = new ServiceHost(typeof(MyService), new Uri[] { new Uri(serviceUrl) });
ServiceEndpoint serviceEndpoint
= _host.AddServiceEndpoint(typeof(IMyService), new WebHttpBinding(), string.Empty);
serviceEndpoint.Behaviors.Add(new WebHttpBehavior());
// Here where I'm stuck, i need something like...
ServiceHostingEnvironmentSection shes = new ServiceHostingEnvironmentSection();
shes.AspNetCompatibilityEnabled = true;
_host.Add(shes);
_host.Open();
Любая помощь очень ценится и благодарит заранее.
Ответы
Ответ 1
Рассмотрим факторизацию явного использования HttpContext.Current за интерфейсом, который вы можете отключить во время модульного тестирования.
HttpContext.Current определяется только тогда, когда ваша служба wcf размещается в веб-приложении asp.net в любом случае - если в какой-то момент вам нужно разместить его как обычную службу wcf, HttpContext.Current не будет доступен.
Ответ 2
Вы можете полностью сделать это, я не знаю, о чем эти другие ответы, но они уходят!
Просто сделайте что-нибудь вроде:
_host = new ServiceHost(...);
// Remove existing behavior as it is readOnly
for (int i = 0; i < _host.Description.Behaviors.Count; i++)
{
if (_host.Description.Behaviors[i] is AspNetCompatibilityRequirementsAttribute)
{
_host.Description.Behaviors.RemoveAt(i);
break;
}
}
// Replace behavior with one that is configured the way you desire.
_host.Description.Behaviors.Add(new AspNetCompatibilityRequirementsAttribute { RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed });
_host.Open();
- Изменить
Это устраняет существующее поведение, если оно существует, а затем добавляет новое поведение, в котором вы предпочитаете режим. Мой пример устанавливает его в. Выяснилось, но вы, конечно, можете установить его в желаемый режим.
Ответ 3
Этот параметр, который можно установить в статическом классе ServiceHostingEnvironment
в System.ServiceModel:
ServiceHostingEnvironment.AspNetCompatibilityEnabled = true;
Это нужно сделать, прежде чем создавать и открывать хосты.
Забастовкa >
Было бы хорошо - но это параметр только для чтения, и единственный способ установить его, похоже, через конфигурацию:-(
Ответ 4
Чтобы ответить на вопрос Остина Харриса:
Вам нужно изменить поведение ServiceHost.
Поскольку атрибут доступен только для чтения, вам нужно удалить и прочитать поведение ServiceHost.
Если у вас есть консольное приложение или служба Windows, у вас будет этот служебный хост.
Что-то вроде этого:
public static void Main()
{
using (ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService)))
{
try
{
// Open the ServiceHost to start listening for messages.
serviceHost.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
// Close the ServiceHost.
serviceHost.Close();
}
catch (TimeoutException timeProblem)
{
Console.WriteLine(timeProblem.Message);
Console.ReadLine();
}
catch (CommunicationException commProblem)
{
Console.WriteLine(commProblem.Message);
Console.ReadLine();
}
}
}
в этом случае код Остина Харриса был бы достаточным (если бы он не написал "Разрешено" вместо "Обязательно..." ).
Однако, если у вас есть WCF-Service, интегрированный в приложение ASP.NET, сложной задачей является получение ServiceHost.
Ключ - это атрибут factory в файле разметки YOUR_SERVICE.svc.
<%@ ServiceHost Factory="ApertureImportBelegung.DerivedFactory" Language="VB" Debug="true" Service="ApertureImportBelegung.ImportBelegung" CodeBehind="Service1.svc.vb" %>
Затем вам нужно написать свой собственный factory.
Ниже приведен код VB.NET, я оставлю его читателю, чтобы перевести его на С# (вам нужно установить WITH_FORMS_AUTHENTICATION в true, и С# ps: http://converter.telerik.com)
'Imports System.ServiceModel
Imports System.ServiceModel.Description
'Imports System.ServiceModel.Dispatcher
'Imports System.ServiceModel.Channels
'Imports System.ServiceModel.Configuration
Imports System.ServiceModel.Activation ' Add reference to assembly System.ServiceModel.Activation.dll
Public Class DerivedHost
Inherits ServiceHost
Public Sub New(t As Type, ParamArray baseAddresses() As Uri)
MyBase.New(t, baseAddresses)
End Sub
Protected Overrides Sub OnOpening()
'Me.Description.Behaviors.Add(New mys)
'Me.Description.Add(New MyServiceBehavior())
'Me.Description.Behaviors.Add(New WcfMessageLoggerExtension())
MyBase.OnOpening()
End Sub
End Class ' DerivedHost
' http://msdn.microsoft.com/en-us/library/aa702697(v=vs.110).aspx
Public Class DerivedFactory
Inherits ServiceHostFactory
Protected Overrides Function CreateServiceHost(t As Type, baseAddresses As Uri()) As ServiceHost
Return New DerivedHost(t, baseAddresses)
End Function ' CreateServiceHost
'Then in the CreateServiceHost method, we can do all of the
'things that we can do in a self-hosted case:
Public Overrides Function CreateServiceHost(service As String, baseAddresses As Uri()) As ServiceHostBase
Application.ConfigData.ReadConfigData()
' The service parameter is ignored here because we know our service.
Dim serviceHost As New ServiceHost(GetType(ImportBelegung), baseAddresses)
' System.ServiceModel.ServiceHostingEnvironment.AspNetCompatibilityEnabled = True
' http://stackoverflow.com/questions/13597408/wcf-message-inspector-is-not-working
'Dim serviceHost As New System.ServiceModel.ServiceHost(GetType(ImportBelegung))
'serviceHost.Description.Behaviors.Add(New WcfMessageLoggerExtension())
' http://stackoverflow.com/questions/5907791/how-to-programatically-create-a-wcf-service-and-its-metadata-on-the-same-url
' http://msdn.microsoft.com/en-us/library/system.servicemodel.servicehost(v=vs.110).aspx
' host.Open()
'This example iterates through all the ServiceEndpoint objects and adds ConsoleMessageTracing as an endpoint behavior:
For Each endpoint As ServiceEndpoint In serviceHost.Description.Endpoints
'endpoint.Behaviors.Add(New WcfMessageLoggerExtension())
'endpoint.Behaviors.Add(New ConsoleOutputBehaviorExtensionElement)
endpoint.Behaviors.Add(New MessageInspector.ConsoleOutputBehavior)
endpoint.Behaviors.Add(New HeaderInspector.ConsoleOutputHeaderBehavior)
Next endpoint
' Ensure (in <system.serviceModel>) aspNetCompatibilityEnabled="true" --> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
#Const WITH_FORMS_AUTHENTICATION = False
#If WITH_FORMS_AUTHENTICATION Then
For i As Integer = 0 To serviceHost.Description.Behaviors.Count - 1 Step 1
If TypeOf serviceHost.Description.Behaviors(i) Is AspNetCompatibilityRequirementsAttribute Then
serviceHost.Description.Behaviors.RemoveAt(i)
Exit For
End If
Next i
serviceHost.Description.Behaviors.Add(New AspNetCompatibilityRequirementsAttribute() With {.RequirementsMode = AspNetCompatibilityRequirementsMode.Required})
#End If
Return serviceHost
End Function ' CreateServiceHost
End Class ' DerivedFactory
Ответ 5
После поворота вокруг Reflector я смог установить флаг AspNetCompatibilityEnabled, используя отражение. У этого подхода есть очевидные недостатки, но он сделал для меня работу:
// get the ServiceHostingEnvironmentSection by calling an internal static method
var section = (ServiceHostingEnvironmentSection)typeof(ServiceHostingEnvironmentSection).GetMethod("UnsafeGetSection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).Invoke(null, null);
// set the read-only flag to false so values can be updated
typeof(ServiceHostingEnvironmentSection).BaseType.BaseType.GetField("_bReadOnly", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(section, false);
// set the AspNetCompatibilityEnabled value
section.AspNetCompatibilityEnabled = true;
// now one can add a Service Route
routes.Add(new ServiceRoute("MyRoutePrefix", new ServiceHostFactory(), typeof(MyService)));