I'm trying to set up some WCF services that are connected and pass a custom HTTP header from service to service.
That is my client call ServiceX, which calls ServiceY, which writes to the DB.
They are originally called from a Silverlight 5 client in some cases, other cases from an ASP.NET web app.
I implemented IClientMessageInspector and IDispatchMessageInspector to pass the header from service to service, and in the DispatchMessageInspector I wrote the header to an implementation of IExtension(Of OperationContext) (see below).
However, I wanted this data only to exist for the duration of the call, but it seems to be sticking around in the services under certain circumstances as I keep seeing the same header data repeated in different calls.
Ultimately, I want to be able to pass a custom header to a WCF service, persist it only while that call exists, send it to the next service in the header, and wipe out that service instance. Am I wrong in my thinking that using a PerCall WCF service setup and OperationContext is the right way to do that?
Here is my implementation of IExtension(Of OperationContext). The auditTransactionId is the thing I want to pass in the service. As well, the Current() property is where I keep seeing existing data:
Imports System.ServiceModel
Public Class CustomOperationContextExtension
Implements IExtension(Of OperationContext)
Private ReadOnly m_items As IDictionary(Of String, Object)
Private m_auditTransactionId As String
Private Sub New()
m_items = New Dictionary(Of String, Object)()
End Sub
Public ReadOnly Property Items() As IDictionary(Of String, Object)
Get
Return m_items
End Get
End Property
Public Property AuditTransactionId() As String
Get
Return m_auditTransactionId
End Get
Set(value As String)
m_auditTransactionId = value
End Set
End Property
Public Shared ReadOnly Property Current() As CustomOperationContextExtension
Get
If (OperationContext.Current IsNot Nothing) Then
Dim context As CustomOperationContextExtension = OperationContext.Current.Extensions.Find(Of CustomOperationContextExtension)()
If context Is Nothing Then
context = New CustomOperationContextExtension()
OperationContext.Current.Extensions.Add(context)
End If
Return context
End If
Return Nothing
End Get
End Property
Public Sub Attach(owner As OperationContext) Implements IExtension(Of System.ServiceModel.OperationContext).Attach
End Sub
Public Sub Detach(owner As OperationContext) Implements IExtension(Of System.ServiceModel.OperationContext).Detach
End Sub
End Class
EDIT:
When I say that data is sticking around, I mean that when I call Current in a new service call I expect the Extensions list to be empty (in the code below in the Current() property), but there is always an existing instance of CustomOperationContextExtension there already that is left over fro a previous call. I'm not sure under which circumstances this happens.
Related
I am using cefsharp in my Winform application. I try to store cookies, cache... so that I do not have to login a web site each time I run my application.
I try to implement as follows:
Dim requestContextSettings = New RequestContextSettings()
requestContextSettings.CachePath = Application.StartupPath + "\Resources"
_browser = New ChromiumWebBrowser("https://cookies_enabled_web.com", New RequestContext(requestContextSettings, New CustomRequestContextHandler()))
Then I implement the class
Public Class CustomRequestContextHandler
Implements IRequestContextHandler
Public Sub OnRequestContextInitialized(requestContext As IRequestContext) Implements IRequestContextHandler.OnRequestContextInitialized
Exit Sub
End Sub
Public Function OnBeforePluginLoad(mimeType As String, url As String, isMainFrame As Boolean, topOriginUrl As String, pluginInfo As WebPluginInfo, ByRef pluginPolicy As PluginPolicy) As Boolean Implements IRequestContextHandler.OnBeforePluginLoad
Return True
End Function
Public Function GetResourceRequestHandler(browser As IBrowser, frame As IFrame, request As IRequest, isNavigation As Boolean, isDownload As Boolean, requestInitiator As String, ByRef disableDefaultHandling As Boolean) As IResourceRequestHandler Implements IRequestContextHandler.GetResourceRequestHandler
Return Nothing
End Function
End Class
But it does not work as expected?
Does anyone know how to fix the problem?
Any help would be appreciated.
Finally, I have successfully stored cache data in Cefsharp version 79. I execute the following code before create the instance of browser.
Dim setting As New CefSettings()
setting.CachePath = Path.Combine(Application.StartupPath, "Resources")
CefSharp.Cef.Initialize(setting)
The following code works fine for one user on the web site.
However as soon an multiple users start logging in simultaneously, Serilog starts missing log file entries. Anybody know what I am missing please?
Public Class cLogger
Public Shared gSerilogger As ILogger = Nothing
Public Shared gblSeriLock As New Object
Public Shared Sub DoSerilog(ByVal sLogMessage As String)
SyncLock gblSeriLock
If gSerilogger Is Nothing Then
gSerilogger = New LoggerConfiguration().WriteTo.Console().WriteTo.File("d:\Logfiles\SERI.log").CreateLogger()
End If
gSerilogger.Information(sLogMessage)
End SyncLock
End Sub
End Class
Usage example:
cLogger.DoSerilog("Test")
Right now I'm able to establish a connection within my class by calling it in each method by doing the following.
Dim sConnectionString As String = ConfigurationManager.AppSettings("Blah")
'Establish connection with db
Dim cnSqlConnection1 As New SqlConnection(sConnectionString)
Only problem is that I have to call it in each method. I was told that it was better to create a constructor for the class nad have the connection string it uses passed into the constructor.
Here's my attempt but can't seem to figure out since I'm still unable to reach it in the method.
Public Sub New(ByVal sConnectionString As String)
sConnectionString = ConfigurationManager.AppSettings("Blah")
End Sub
What is the best way to do it? Thanks in advance.
You should store the passed connectionstring in a global variable available in all of your class methods
Public Clas MyClass
Private String gs_conString
Public Sub New(ByVal sConnectionString As String)
gs_conString = sConnectionString
End Sub
Public Sub AMethod()
'Establish connection with db
Dim cnSqlConnection1 As New SqlConnection(gs_conString)
.....
End Sub
.....
End Class
Of course this means that every time you create an instance of this class you need to pass the connection string to your constructor
Dim cl As MyClass = new MyClass(ConfigurationManager.AppSettings("Blah"))
So it is probably better to use the constructor to extract the connection string automatically everytime you create an instance
Private String gs_conString
Public Sub New()
gs_conString = ConfigurationManager.AppSettings("Blah")
End Sub
Go with the first option, putting the connection string in the constructor. You don't want your class to depend directly on <appSettings>.
Your class's interface should indicate what dependencies it has. When you put the connection string in the constructor, the class says, "Hey, I need a connection string!"
If the class calls <appSettings> then a user of the class has no way of knowing that the class expects to find a connection string there unless they open your code and read it. If they don't know that the connection string belongs there then they'll get a null reference exception with no explanation.
That raises the question - whatever class creates your class, where does it get the connection string so it can pass it to the constructor? Dependency injection is the answer. It enables you to write classes that way then "wire it up" so that the correct arguments get passed to your constructors.
I'm writing a WCF WebMethod to upload files to, of which I taken snippets from around the web. The WCF interface looks like this:
<ServiceContract()>
Public Interface ITransferService
<OperationContract()>
Sub UploadFile(ByVal request As RemoteFileInfo)
End Interface
<MessageContract()>
Public Class RemoteFileInfo
Implements IDisposable
<MessageHeader(MustUnderstand:=True)>
Public FileName As String
<MessageHeader(MustUnderstand:=True)>
Public Length As Long
<MessageBodyMember(Order:=1)>
Public FileByteStream As System.IO.Stream
Public Sub Dispose() Implements IDisposable.Dispose
If FileByteStream IsNot Nothing Then
FileByteStream.Close()
FileByteStream = Nothing
End If
End Sub
End Class
Within ASP.NET, when the web method is consumed, for some reason it only works when the interface is used as part of the instantiation of RemoteFileInfo:
Protected Sub btn_Click(sender As Object, e As EventArgs) Handles btn.Click
If fu.HasFile Then
Dim fi As New System.IO.FileInfo(fu.PostedFile.FileName)
' this is the line in question --------------
Dim cu As ServiceReference1.ITransferService = New ServiceReference1.TransferServiceClient()
' -------------------------------------------
Dim uri As New ServiceReference1.RemoteFileInfo()
Using stream As New System.IO.FileStream(fu.PostedFile.FileName, IO.FileMode.Open, IO.FileAccess.Read)
uri.FileName = fu.FileName
uri.Length = fi.Length
uri.FileByteStream = stream
cu.UploadFile(uri)
End Using
End If
End Sub
Can anyone advise why it is not possible to create an instance of TransferService using the following approach:
Dim cu As New ServiceReference1.TransferServiceClient()
If I try the above, it breaks this line:
cu.UploadFile(uri)
...and UploadFile must be called with three parameters (FileName, Length, FileByteStream) even there is no method that uses this signature.
Why is the Interface required when creating an instance of this class please?
When you create the proxy for your service with the "Add Service Reference" dialog, by default the proxy creation code will "unwrap" message contracts, like the one you have. If you want the message contract to appear as you defined on the server side on your proxy, you need to select the "Advanced" tab, and check the "Always generate message contracts" option. With that you'll get the message contract in your client as well.
The issue is that when a MessageContract is encountered as a parameter, the WCF client generation assumes by default that you want to implement a messaging-style interface, and provides the discrete properties from the message contract as part of the client-side interface.
The Using Messaging Contracts article in MSDN contains a very detailed description of what can be done with a messaging contract and I suspect that Microsoft chose this default behavior because of some of the "games" that you can play with the messages.
However, if you examine the code generated for your UploadFile on the client side, there are some interesting tidbits that help to explain what is going on.
The first is the comments for the UploadFile method in the interface:
'CODEGEN: Generating message contract since the operation UploadFile is neither RPC nor document wrapped.
...
Function UploadFile(ByVal request As ServiceReference1.RemoteFileInfo) As ServiceReference1.UploadFileResponse
This implies that the contract would have been generated differently if the message contract had a different implementation.
The second is that you will see that there is nothing special about the code that is used to actually make the service call:
Public Sub UploadFile(ByVal FileName As String, ByVal Length As Long, ByVal FileByteStream As System.IO.Stream)
Dim inValue As ServiceReference1.RemoteFileInfo = New ServiceReference1.RemoteFileInfo()
inValue.FileName = FileName
inValue.Length = Length
inValue.FileByteStream = FileByteStream
Dim retVal As ServiceReference1.UploadFileResponse = CType(Me,ServiceReference1.ITransferService).UploadFile(inValue)
End Sub
So in this case, your code is doing exactly what the generated code does. However, if the MessageContract were more complex, I suspect that this would no longer be the case.
So, for your question:
Can anyone advise why it is not possible to create an instance of
TransferService using the following approach...
There is no reason not to take this approach as long as you verify that the implementation of the method call is functionality equivalent to your code.
There are a couple of options for changing the default generation of the method in the client:
1) Remove the MessageContract attribute from the RemoteFileInfo class.
2) Although it seems to be counter-intuitive, you can check the Always generate message contracts checkbox in the Configure Service Reference Dialog Box.
When sending messages from a self hosted WCF service to many clients (about 10 or so), sometimes messages are being delayed significantly longer than I'd expect (several seconds to send to a client on local network). Does anyone have an idea why this would be and how to fix it?
Some background: the application is a stock ticker style service. It receives messages from a 3rd party server and re-publishes them to clients that connect to the service. It's very important that messages are published as quickly as possible, and in most cases the time between receiving a message and publishing it to all clients is less than 50ms (it's so quick it approaches the resolution of DateTime.Now).
Over the past few weeks, we've been monitoring some occasions when messages are delayed by 2 or 3 seconds. A few days ago, we got a big spike and messages were being delayed by 40-60 seconds. Messages are not being dropped as far as I can tell (unless the entire connection is dropped). The delays does not appear to be specific to any one client; it affects all clients (including ones on the local network).
I send messages to the clients by spamming the ThreadPool. As quickly as messages arrive I call BeginInvoke() once per message per client. The theory being that if any one client is slow to receive a message (because it's on dialup and downloading updates or something) that it won't impact other clients. That isn't what I'm observing though; it appears that all clients (including ones on the local network) are impacted by the delay by a similar duration.
The volume of messages I'm dealing with is 100-400 per second. Messages contain a string, a guid, a date and, depending on the message type, 10-30 integers. I've observed them using Wireshark as being less than 1kB each. We have 10-20 clients connected at any one time.
The WCF server is being hosted in a Windows service on a Windows 2003 Web Edition Server. I'm using the NetTCP binding with SSL/TLS encryption enabled and a custom username / password authentication. It has a 4Mbit internet connection, dual core CPU and 1GB ram and is dedicated to this application. The service is set to ConcurrencyMode.Multiple. The service process, even under high load, rarely exceeds 20% CPU usage.
So far, I've tweaked various WCF configuration options such as:
serviceBehaviors/serviceThrottling/maxConcurrentSessions (currently 102)
serviceBehaviors/serviceThrottling/maxConcurrentCalls (currently 64)
bindings/netTcpBinding/binding/maxConnections (currently 100)
bindings/netTcpBinding/binding/listenBacklog (currently 100)
bindings/netTcpBinding/binding/sendTimeout (currently 45s, although I've tried it as high as 3 minutes)
It appears to me like the messages are being queued inside WCF once some threshold is reached (hence why I've being increasing the throttling limits). But to affect all clients it would need to max out all outgoing connections with one or two slow clients. Does anyone know if this is true of the WCF internals?
I can also improve efficiency by coalescing incoming messages when I send them to the client. However, I suspect there's something underlying going on and coalescing won't fix the problem in the long term.
WCF Config (with company names changed):
<system.serviceModel>
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8100/Publisher"/>
</baseAddresses>
</host>
<endpoint address="ThePublisher"
binding="netTcpBinding"
bindingConfiguration="Tcp"
contract="Company.Product.Server.Publisher.IPublisher" />
</behavior>
Code used to send messages:
Private Sub HandleDataBackground(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
If Me._FeedDataQueue.Count > 0 Then
' Dequeue any items received in last 50ms.
While True
Dim dataAndReceivedTime As DataWithReceivedTimeArg
SyncLock Me._FeedDataQueue
If Me._FeedDataQueue.Count = 0 Then Exit While
dataAndReceivedTime = Me._FeedDataQueue.Dequeue()
End SyncLock
' Publish data to all clients.
Me.SendDataToClients(dataAndReceivedTime)
End While
End If
End Sub
Private Sub SendDataToClients(ByVal data As DataWithReceivedTimeArg)
Dim clientsToReceive As IEnumerable(Of ClientInformation)
SyncLock Me._ClientInformation
clientsToReceive = Me._ClientInformation.Values.Where(Function(c) Contract.CollectionContains(c.ContractSubscriptions, data.Data.Contract) AndAlso c.IsUsable).ToList()
End SyncLock
For Each clientInfo In clientsToReceive
Dim futureChangeMethod As New InvokeClientCallbackDelegate(Of DataItem)(AddressOf Me.InvokeClientCallback)
futureChangeMethod.BeginInvoke(clientInfo, data.Data, AddressOf Me.SendDataToClient)
Next
End Sub
Private Sub SendDataToClient(ByVal callback As IFusionIndicatorClientCallback, ByVal data As DataItem)
' Send
callback.ReceiveData(data)
End Sub
Private Sub InvokeClientCallback(Of DataT)(ByVal client As ClientInformation, ByVal data As DataT, ByVal method As InvokeClientCallbackMethodDelegate(Of DataT))
Try
' Send
If client.IsUsable Then
method(client.CallbackObject, data)
client.LastContact = DateTime.Now
Else
' Make sure the callback channel has been removed.
SyncLock Me._ClientInformation
Me._ClientInformation.Remove(client.SessionId)
End SyncLock
End If
Catch ex As CommunicationException
....
Catch ex As ObjectDisposedException
....
Catch ex As TimeoutException
....
Catch ex As Exception
....
End Try
End Sub
A sample of one of the message types:
<DataContract(), KnownType(GetType(DateTimeOffset)), KnownType(GetType(DataItemDepth)), KnownType(GetType(DataItemDepthDetail)), KnownType(GetType(DataItemHistory))> _
Public MustInherit Class DataItem
Implements ICloneable
Protected _Contract As String
Protected _MessageId As Guid
Protected _TradeDate As DateTime
<DataMember()> _
Public Property Contract() As String
...
End Property
<DataMember()> _
Public Property MessageId() As Guid
...
End Property
<DataMember()> _
Public Property TradeDate() As DateTime
...
End Property
Public MustOverride Function Clone() As Object Implements System.ICloneable.Clone
End Class
<DataContract()> _
Public Class DataItemDepth
Inherits DataItem
Protected _VolumnPriceDetail As IList(Of DataItemDepthItem)
<DataMember()> _
Public Property VolumnPriceDetail() As IList(Of DataItemDepthItem)
...
End Property
Public Overrides Function Clone() As Object
...
End Function
End Class
<DataContract()> _
Public Class DataItemDepthItem
Protected _Volume As Int32
Protected _Price As Int32
Protected _BidOrAsk As BidOrAsk ' BidOrAsk is an Int32 enum
Protected _Level As Int32
<DataMember()> _
Public Property Volume() As Int32
...
End Property
<DataMember()> _
Public Property Price() As Int32
...
End Property
<DataMember()> _
Public Property BidOrAsk() As BidOrAsk ' BidOrAsk is an Int32 enum
...
End Property
<DataMember()> _
Public Property Level() As Int32
...
End Property
End Class
After a long support request with Microsoft support, we managed to identify the issue.
Calling WCF channel methods using Begin/End Invoke delegate pattern actually turns into synchronous calls, not asynchronous.
The correct way to asynchronously call WCF methods is by any way except async delegates, which may include the thread pool, raw threads or WCF async callbacks.
In the end I used WCF async callbacks (which can be applied to a callback interface, although I couldn't find specific examples of that).
The following link makes this more explicit:
https://learn.microsoft.com/en-us/archive/blogs/drnick/begininvoke-bugs