In my solution I have three projects. One is a WCF service, another is a Winforms app from where I am calling the web service, and the last is a class library where class has been design extending soap extension class.
My objective is to capture response & request xml when I am calling the WCF service from my Winforms app. I am getting object reference not set error when I am trying to capture xml.
Here is my class library source code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;
using System.Net;
using System.Xml;
namespace SoapLogger
{
public class TraceExtension : SoapExtension
{
private Stream oldStream;
private Stream newStream;
private static XmlDocument xmlRequest;
/// <summary>
/// Gets the outgoing XML request sent to PayPal
/// </summary>
public static XmlDocument XmlRequest
{
get { return xmlRequest; }
}
private static XmlDocument xmlResponse;
/// <summary>
/// Gets the incoming XML response sent from PayPal
/// </summary>
public static XmlDocument XmlResponse
{
get { return xmlResponse; }
}
/// <summary>
/// Save the Stream representing the SOAP request
/// or SOAP response into a local memory buffer.
/// </summary>
/// <param name="stream">
/// <returns></returns>
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
/// <summary>
/// If the SoapMessageStage is such that the SoapRequest or
/// SoapResponse is still in the SOAP format to be sent or received,
/// save it to the xmlRequest or xmlResponse property.
/// </summary>
/// <param name="message">
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
xmlRequest = GetSoapEnvelope(newStream);
CopyStream(newStream, oldStream);
break;
case SoapMessageStage.BeforeDeserialize:
CopyStream(oldStream, newStream);
xmlResponse = GetSoapEnvelope(newStream);
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
/// <summary>
/// Returns the XML representation of the Soap Envelope in the supplied stream.
/// Resets the position of stream to zero.
/// </summary>
/// <param name="stream">
/// <returns></returns>
private XmlDocument GetSoapEnvelope(Stream stream)
{
XmlDocument xml = new XmlDocument();
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
xml.LoadXml(reader.ReadToEnd());
stream.Position = 0;
return xml;
}
/// <summary>
/// Copies a stream.
/// </summary>
/// <param name="from">
/// <param name="to">
private void CopyStream(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
#region NoOp
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="methodInfo">
/// <param name="attribute">
/// <returns></returns>
public override object GetInitializer(LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute)
{
return null;
}
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="WebServiceType">
/// <returns></returns>
public override object GetInitializer(Type WebServiceType)
{
return null;
}
/// <summary>
/// Included only because it must be implemented.
/// </summary>
/// <param name="initializer">
public override void Initialize(object initializer)
{
}
#endregion NoOp
}
Here is how I am calling the web service from my Winforms app:
private void button1_Click(object sender, EventArgs e)
{
using (ServiceRef.TestServiceSoapClient oService = new ServiceRef.TestServiceSoapClient())
{
textBox1.Text = oService.HelloWorld("Sudip");
var soapRequest = SoapLogger.TraceExtension.XmlRequest.InnerXml;
var soapResponse = SoapLogger.TraceExtension.XmlResponse.InnerXml;
}
}
These two lines are causing the object reference error
var soapRequest = SoapLogger.TraceExtension.XmlRequest.InnerXml;
var soapResponse = SoapLogger.TraceExtension.XmlResponse.InnerXml;
I just could not figure out why I am getting error.
The Winforms app has an app.config where I register my class library assembly to capture the xml. Here is my app.config details
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="TestServiceSoap"/> </basicHttpBinding>
</bindings> <client> <endpoint address="http://localhost:6804/Service1.asmx"
binding="basicHttpBinding" bindingConfiguration="TestServiceSoap"
contract="ServiceRef.TestServiceSoap" name="TestServiceSoap"/>
</client>
</system.serviceModel>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<webServices> <soapExtensionTypes>
<add type="SoapLogger.TraceExtension,SoapLogger" priority="1" group="0" />
</soapExtensionTypes> </webServices>
</system.web>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup> </configuration>
When I search google just to know how to capture request/response xml then I got this kind of many article. I followed many but nothing works.
This url I followed to get the job done http://jramirezdev.net/blog/c-tip-capturar-los-mensajes-soap-de-un-servicio-asmx-que-hemos-referenciado
What kind of mistake am I making not clear. I set the break point at every method in my class library method but when web service call no method got executed in the class library. I don't want to use any tool like wireshark, fiddler to capture the request/response xml rather want to do same thing programatically.
So please guide me what is my mistake? Why am I getting object reference not set error? Please have a look at my code or go to the url link and tell me what is wrong in my approach
Simply we can trace the request message as.
OperationContext context = OperationContext.Current;
if (context != null && context.RequestContext != null)
{
Message msg = context.RequestContext.RequestMessage;
string reqXML = msg.ToString();
}
Configure WCF Tracing
Send/receive some messages
Get the SvcTraceViewer.exe log viewer and open the trace output.
Profit.
The error was caused because xmlRequest/xmlResponse have not been initialized when you access them. Hence, try to check null, or create default instances for them.
From your code they are only initialized in the ProcessMessage(SoapMessage message){}, make sure that the method is called before you call
var soapRequest = SoapLogger.TraceExtension.XmlRequest.InnerXml;
var soapResponse = SoapLogger.TraceExtension.XmlResponse.InnerXml;
Hope this helps.
EDIT:
After closely examining your code, I realized that the extension you wrote (SoapExtension) was for ASP.NET XML web services, and it had no business with a WCF service.
To inspect the request and reply in a WCF service, you can extend your dispatcher on the service side by implementing System.ServiceModel.Dispatcher.IDispatchMessageInspector.
You can find the example here http://msdn.microsoft.com/en-us/library/ms733104%28v=vs.100%29.aspx
There is an another way to see XML SOAP - custom MessageEncoder. The main difference from IDispatchMessageInspector / IClientMessageInspector is that it works on lower level, so it captures original byte content including any malformed xml.
In order to implement tracing using this approach you need to wrap a standard textMessageEncoding with custom message encoder as new binding element and apply that custom binding to endpoint in your config.
Also you can see as example how I did it in my project -
wrapping textMessageEncoding, logging encoder, custom binding element and config.
Related
I have been introduced to an already existing C# ASP.Net Web API project a week ago. It returns all the data in JSON and Pascal Case, and this data is used by websites using React.Js which are case sensitive.
During the last week, after a commit that changed litteraly nothing on the API project (it only added translations for the Web Client project in the solution), suddenly the project started returning JSON in Camel case and not in Pascal Case anymore.
The problem is not like this post, I don't send to the API camel instead of pascal, the API sends camel instead of pascal.
I've been searching how to fix it but there are two specifities to the project that makes it difficult to find an answer :
The project uses ASP.Net and not ASP.Net Core, making posts like this or this not helpful
The project uses NancyFx 2.0.0, so it doesn't have any starting file (like the startup class in core projects, or .asax file) but uses a custom bootstrapper (see code down below)
Bootstrapper
public class AppBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
log4net.Config.XmlConfigurator.Configure();
base.ApplicationStartup(container, pipelines);
pipelines.BeforeRequest.AddItemToStartOfPipeline(ctx =>
{
if (ctx != null)
{
Log.Request(ctx.Request.GetHashCode(), ctx.Request.Method, ctx.Request.Path, ctx.Request.UserHostAddress, ctx.Request.Headers.UserAgent);
}
return null;
});
pipelines.AfterRequest.AddItemToEndOfPipeline(ctx =>
{
if (ctx != null)
{
Log.Response(ctx.Request.GetHashCode(), ctx.Response.StatusCode);
}
});
}
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
pipelines.AfterRequest.AddItemToEndOfPipeline(ctx =>
{
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,HEAD,OPTIONS")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type, Authorization");
});
// Gzip management, useless for this post
}
JsonSerializer
internal class JsonNetSerializer : ISerializer
{
private readonly JsonSerializer _serializer;
/// <summary>
/// Initializes a new instance of the <see cref="JsonNetSerializer"/> class.
/// </summary>
public JsonNetSerializer()
{
_serializer = JsonSerializer.CreateDefault();
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonNetSerializer"/> class,
/// with the provided <paramref name="serializer"/>.
/// </summary>
/// <param name="serializer">Json converters used when serializing.</param>
public JsonNetSerializer(JsonSerializer serializer)
{
_serializer = serializer;
}
/// <summary>
/// Whether the serializer can serialize the content type
/// </summary>
/// <param name="mediaRange">Content type to serialise</param>
/// <returns>True if supported, false otherwise</returns>
public bool CanSerialize(MediaRange mediaRange)
{
return JsonHelpers.IsJsonType(mediaRange);
}
/// <summary>
/// Gets the list of extensions that the serializer can handle.
/// </summary>
/// <value>An <see cref="IEnumerable{T}"/> of extensions if any are available, otherwise an empty enumerable.</value>
public IEnumerable<string> Extensions
{
get { yield return "json"; }
}
/// <summary>
/// Serialize the given model with the given contentType
/// </summary>
/// <param name="mediaRange">Content type to serialize into</param>
/// <param name="model">Model to serialize</param>
/// <param name="outputStream">Output stream to serialize to</param>
/// <returns>Serialised object</returns>
public void Serialize<TModel>(MediaRange mediaRange, TModel model, Stream outputStream)
{
using (var writer = new JsonTextWriter(new StreamWriter(new UnclosableStreamWrapper(outputStream))))
{
_serializer.Serialize(writer, model);
}
}
}
I have looked for solutions and so long I've found some sites talking about Owin, but it seems that I have to use a owin-based server (like Nowin) to host the ASP.Net application. The problem is that I have literally never used/heard about it and I lack of time to learn how to use it. On top of that I'm not sure at all that Nowin's Start<TContext>() function will be useful to modify the API's return formatting...
Every other solution I've found was leading to ASP.Net Core technology, but nothing for ASP.Net
Short:
For each and every request a new OWIN context is created, I would like to be able to prevent this for certain resource types or paths (images, css, js).
Full:
In our application start-up we register a dbcontext creation delegate so that the dbcontext will be created just once per request.
public virtual void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(Factory.DbContextCreateDelegate);
}
If a client makes a request for the style sheet, an OWIN context will be created an thus a new dbcontext will also be created. I would like to be able to either not create the OwinContext at all, or at least to be able to prevent some of it's "on create" callbacks to be executed for certain request types/paths.
Alternatively, as I can see why the approach of (partially) "disabling" OWIN would lead to problems, I would like to hear what the best practice would be? How can I serve a static file without creating a database context for each request? (small remark here is that our static files are embedded resources served using a virtual path provider... The problem also occurs for "normal" static files.)
Background: Yesterday I started noticing that every now and then some parts of our application did not load. Sometimes it was single image, sometimes the entire CSS file. After some investigation I saw that some requests were throwing http 500 errors, the exception thrown was often things like a SQL connection time out (but also other exceptions).
While we are of course trying to fix those exceptions. I do believe it to be complete nonsense for our application to set up a database connection when a client makes a request for a single image... That's about 10 database connections for a single page request???
Seems to me like such an obvious problem but I have been Googling all of yesterday and found nothing close to a solution or work around. What am I missing stack?
EDIT: I just tried an approach where I did not actually create the dbcontext but a stub instead. In hindsight this obviously is not the solution to this problem as the OwinContext tries to continue it's process and will critically fail when it tries to get the user from the database using that stub dbcontext. The dbcontext is not the problem, I need to completely bypass Owin... I think...
Microsoft.Owin.StaticFiles to the rescue!
I would still like to know why this is not enabled by default in the MVC OWIN template application. But for most cases it is just a matter of a single line of code to enable static files in OWIN.
BASIC SCENARIO
Setting up Owin to treat a folder and its contents as static files:
public virtual void Configuration(IAppBuilder app)
{
app.UseStaticFiles("/PathToYourStaticFilesFolder");
}
SCENARIO WITH EMBEDDED RESOURCES
Unfortunately for me we are serving most of our static content as embedded resources using an implementation of a VirtualPathProvider. Luckily this is also relatively easy to implement, though it does require writing a wrapper around your VirtualPathProvider to implement the required IFileSystem and IFileInfo interfaces that OWIN requires.
Relevant parts of code from my final solution, not posting the entire VirtualPathProvider as there are plenty of examples for that online.
The wrapper around the VirtualPathProvider:
/// <summary>
/// Represents a virtual file system.
/// A wrapper around <see cref="MyCustomVirtualPathProvider"/> implementing
/// IFileSystem for use in Owin StaticFiles.
/// </summary>
public class VirtualFileSystem : IFileSystem
{
/// <summary>
/// Locate the path in the virtual path provider
/// </summary>
/// <param name="subpath">The path that identifies the file</param>
/// <param name="fileInfo">The discovered file if any</param>
/// <returns>
/// True if a file was located at the given path
/// </returns>
public bool TryGetFileInfo(string subpath, out IFileInfo fileInfo)
{
MyCustomVirtualPathProvider virtualPathProvider =
(MyCustomVirtualPathProvider) HostingEnvironment.VirtualPathProvider;
if (!virtualPathProvider.FileExists(subpath))
{
fileInfo = null;
return false;
}
try
{
EmbeddedResourceVirtualFile virtualFile =
(EmbeddedResourceVirtualFile) virtualPathProvider.GetFile(subpath);
fileInfo = new EmbeddedResourceFileInfo(virtualFile);
return true;
}
catch (InvalidCastException)
{
fileInfo = null;
return false;
}
}
/// <summary>
/// Not used in our implementation
/// </summary>
/// <param name="subpath"></param>
/// <param name="contents"></param>
/// <returns></returns>
public bool TryGetDirectoryContents(string subpath, out IEnumerable<IFileInfo> contents)
{
throw new NotImplementedException();
}
}
The wrapper around the embedded resource:
/// <summary>
/// Represents the file info of an embedded resource
/// </summary>
public class EmbeddedResourceFileInfo : IFileInfo
{
/// <summary>
/// Return file contents as readonly stream. Caller should dispose stream when complete.
/// </summary>
/// <returns>
/// The file stream
/// </returns>
public Stream CreateReadStream()
{
return virtualFile.Open();
}
/// <summary>
/// The length of the file in bytes, or -1 for a directory info
/// </summary>
public long Length => virtualFile.Length;
/// <summary>
/// The name of the file
/// </summary>
public string Name => virtualFile.Name;
/// <summary>
/// When the file was last modified
/// </summary>
public DateTime LastModified => virtualFile.LastModified;
/// <summary>
/// Returns null as these are virtual files
/// </summary>
public string PhysicalPath => null;
/// <summary>
/// True for the case TryGetDirectoryContents has enumerated a sub-directory
/// </summary>
public bool IsDirectory => virtualFile.IsDirectory;
private readonly EmbeddedResourceVirtualFile virtualFile;
/// <summary>
/// Construct using a <see cref="EmbeddedResourceVirtualFile"/>
/// </summary>
/// <param name="virtualFile"></param>
public EmbeddedResourceFileInfo(EmbeddedResourceVirtualFile virtualFile)
{
this.virtualFile = virtualFile;
}
}
And lastly, setting up Owin to use our virtual file system:
public virtual void Configuration(IAppBuilder app)
{
var staticFilesOptions = new StaticFileOptions
{
FileSystem = new VirtualFileSystem()
};
app.UseStaticFiles(staticFilesOptions);
}
I am trying to use IDispatchMessageInspector in a WCF service implementation to access custom header values.
Something like:
public class MyService : IMyService
{
public List<string> GetNames()
{
var headerInspector = new CustomHeaderInspector();
// Where do request & client channel come from?
var values = headerInspector.AfterReceiveRequest(ref request, clientChannel, OperationContext.Current.InstanceContext);
}
}
I've implemented my own IDispatchMessageInspector class.
public class CustomHeaderInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
var userName = prop.Headers["Username"];
return userName;
}
}
How do I pass
System.ServiceModel.Channels.Message and
System.ServiceModel.IClientChannel
to AfterReceiveRequest called from the service implementation?
EDIT:
Many articles like this one or this one, give examples on how to implement your own ServiceBehavior. So your service implementation looks like this:
[MyCustomBehavior]
public class MyService : IMyService
{
public List<string> GetNames()
{
// Can you use 'MyCustomBehavior' here to access the header properties?
}
}
So with this, can I access MyCustomBehavior somehow within the service operation method to access custom header values?
You have to configure the
<extensions>
<behaviorExtensions>
<add
name="serviceInterceptors"
type="CustomHeaderInspector , MyDLL, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
/>
</behaviorExtensions>
</extensions>
Then the extension will be handled in your WCF stack. The service itself has no notion of the serviceInterceptors and you do not have to do something like in your first code block. The WCF stack will inject you Inspector.
MSDN: system.servicemodel.dispatcher.idispatchmessageinspector
I'm using IClientMessageInspector for same goal.
Here is how you can apply them from code:
var serviceClient = new ServiceClientClass(binding, endpointAddress);
serviceClient.Endpoint.Behaviors.Add(
new MessageInspectorEndpointBehavior<YourMessageInspectorType>());
/// <summary>
/// Represents a run-time behavior extension for a client endpoint.
/// </summary>
public class MessageInspectorEndpointBehavior<T> : IEndpointBehavior
where T: IClientMessageInspector, new()
{
/// <summary>
/// Implements a modification or extension of the client across an endpoint.
/// </summary>
/// <param name="endpoint">The endpoint that is to be customized.</param>
/// <param name="clientRuntime">The client runtime to be customized.</param>
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new T());
}
/// <summary>
/// Implement to pass data at runtime to bindings to support custom behavior.
/// </summary>
/// <param name="endpoint">The endpoint to modify.</param>
/// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// Nothing special here
}
/// <summary>
/// Implements a modification or extension of the service across an endpoint.
/// </summary>
/// <param name="endpoint">The endpoint that exposes the contract.</param>
/// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Nothing special here
}
/// <summary>
/// Implement to confirm that the endpoint meets some intended criteria.
/// </summary>
/// <param name="endpoint">The endpoint to validate.</param>
public void Validate(ServiceEndpoint endpoint)
{
// Nothing special here
}
}
And here is sample implementation of MessageInspector I'm using to pass client version to server, and retrieve server version in custom headers:
/// <summary>
/// Represents a message inspector object that can be added to the <c>MessageInspectors</c> collection to view or modify messages.
/// </summary>
public class VersionCheckMessageInspector : IClientMessageInspector
{
/// <summary>
/// Enables inspection or modification of a message before a request message is sent to a service.
/// </summary>
/// <param name="request">The message to be sent to the service.</param>
/// <param name="channel">The WCF client object channel.</param>
/// <returns>
/// The object that is returned as the <paramref name="correlationState " /> argument of
/// the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply(System.ServiceModel.Channels.Message#,System.Object)" /> method.
/// This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid" /> to ensure that no two
/// <paramref name="correlationState" /> objects are the same.
/// </returns>
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(new VersionMessageHeader());
return null;
}
/// <summary>
/// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
/// </summary>
/// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
/// <param name="correlationState">Correlation state data.</param>
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var serverVersion = string.Empty;
var idx = reply.Headers.FindHeader(VersionMessageHeader.HeaderName, VersionMessageHeader.HeaderNamespace);
if (idx >= 0)
{
var versionReader = reply.Headers.GetReaderAtHeader(idx);
while (versionReader.Name != "ServerVersion"
&& versionReader.Read())
{
serverVersion = versionReader.ReadInnerXml();
break;
}
}
ValidateServerVersion(serverVersion);
}
private static void ValidateServerVersion(string serverVersion)
{
// TODO...
}
}
public class VersionMessageHeader : MessageHeader
{
public const string HeaderName = "VersionSoapHeader";
public const string HeaderNamespace = "<your namespace>";
private const string VersionElementName = "ClientVersion";
public override string Name
{
get { return HeaderName; }
}
public override string Namespace
{
get { return HeaderNamespace; }
}
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
writer.WriteElementString(
VersionElementName,
Assembly.GetExecutingAssembly().GetName().Version.ToString());
}
}
I believe you don't need to implement custom IDispatchMessageInspector to retrieve custom headers, it can be done from service operation method like this:
var mp = OperationContext.Current.IncomingMessageProperties;
var property = (HttpRequestMessageProperty)mp[HttpRequestMessageProperty.Name];
var userName = property.Headers["Username"];
It makes sense to implement custom dispatch message inspector if you want to abort message processing, for example if credentials are missing - you can just throw FaultException in this case.
But if you still want to pass value from dispatch message inspector to service operation method - probably it can be passed through some singleton along with call identifier (session id), to be extracted later by method, or using wcf extensions
What I did to access the details I set the following inside IDispatchMessageInspector.AfterReceiveRequest
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username, "Membership Provider"), roles);
I've omitted the authentication code from this.
To access the value from the service method, you can call
Thread.CurrentPrincipal.Identity.Name
On the MSDN page that you linked to there is also a description how an inspector can be inserted and also an example of that. To quote:
Typically, message inspectors are inserted by a service behavior, an endpoint behavior, or a contract behavior. The behavior then adds the message inspector to the DispatchRuntime.MessageInspectors collection.
Later on you have the following examples:
Implementing custom IDispatchMessageInspector
Implementing custom IServiceBehavior that adds the inspector to the runtime.
Configuration of the behavior via .config file.
That should be enough to get you started. Otherwise feel free to ask :)
If you just want to get access to headers from within your service, you could try OperationContext.Current.IncomingMessageHeaders.
I'm getting this error:
The communication object,
System.ServiceModel.ChannelFactory`1[FxCurveService.IFxCurveService],
cannot be used for communication because it is in the Faulted state.
When I call this code:
using (var client = new WCFServiceChannelFactory<IFxCurveService>(new Uri("http://ksqcoreapp64int:5025/")))
{
guid = client.Call(svc => svc.ReserveSnapshot(fxCurveKey));
DiscountFactorNew[] dfs = client.Call(svc => svc.GetDiscountFactors(guid, dates, from));
Assert.IsTrue(guid != null);
}
It errors here - client.Call(svc => svc.ReserveSnapshot(fxCurveKey));
I have no idea why it is doing this. I am passing the right parameters, inputting the correct address for the service, what else should I be checking here?
Btw, WCFServiceChannelFactory is our own class we use to take care of making service calls. Outline here:
public class WCFServiceChannelFactory<T> : IDisposable
{
public WCFServiceChannelFactory();
public WCFServiceChannelFactory(Uri uri);
public T Channel { get; }
public System.ServiceModel.ChannelFactory<T> ChannelFactory { get; }
public Type ChannelType { get; }
public void Call(Action<T> f);
public R Call<R>(Func<T, R> f);
public void Dispose();
}
The thing is, the problem is not with this, as this is working in the same exact fashion in every other project but this one. Basically, I have to pass the Uri directly in mine, where as others derive it from a .config file in the project, which I was unable to do here. That's the only difference.
Thanks.
You can't access details of the exception if the channel is disposed. So the lovely using pattern construction is not recommended when accessing a WCF service. In fact, the Exception properties requires to have access to the channel to extract some information about the exception (don't know if MS missed that point, or if there are technical reasons behind).
I've written a small class to simplify the call to WCF proxies (this site helps me to understand the problem and to write the class) :
using System;
using System.ServiceModel;
namespace Utility
{
public class ServiceHelper
{
/// <summary>
/// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service
/// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
/// </summary>
/// <typeparam name="TService">The type of the service to use</typeparam>
/// <param name="action">Lambda of the action to performwith the service</param>
[System.Diagnostics.DebuggerStepThrough]
public static void UsingProxy<TService>(Action<TService> action)
where TService : ICommunicationObject, IDisposable, new()
{
var service = new TService();
bool success = false;
try
{
action(service);
if (service.State != CommunicationState.Faulted)
{
service.Close();
success = true;
}
}
finally
{
if (!success)
{
service.Abort();
}
}
}
/// <summary>
/// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service
/// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
/// </summary>
/// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
/// <param name="action">Action to perform with the client instance.</param>
/// <remarks>In the configuration, an endpoint with names that maches the <typeparamref name="TIServiceContract"/> name
/// must exists. Otherwise, use <see cref="UsingContract<TIServiceContract>(string endpointName, Action<TIServiceContract> action)"/>. </remarks>
[System.Diagnostics.DebuggerStepThrough]
public static void UsingContract<TIServiceContract>(Action<TIServiceContract> action)
{
UsingContract<TIServiceContract>(
typeof(TIServiceContract).Name,
action
);
}
/// <summary>
/// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service
/// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
/// </summary>
/// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
/// <param name="action">Action to perform with the client instance.</param>
/// <param name="endpointName">Name of the endpoint to use</param>
[System.Diagnostics.DebuggerStepThrough]
public static void UsingContract<TIServiceContract>(
string endpointName,
Action<TIServiceContract> action)
{
var cf = new ChannelFactory<TIServiceContract>(endpointName);
var channel = cf.CreateChannel();
var clientChannel = (IClientChannel)channel;
bool success = false;
try
{
action(channel);
if (clientChannel.State != CommunicationState.Faulted)
{
clientChannel.Close();
success = true;
}
}
finally
{
if (!success) clientChannel.Abort();
}
}
}
}
Then you can simply do something like this (depending if you have a service reference or the contracts :
ServiceHelper.UsingContract<IFxCurveService>(svc=>
{
guid = svc.ReserveSnapshot(fxCurveKey);
DiscountFactorNew[] dfs = svc.GetDiscountFactors(guid, dates, from));
Assert.IsTrue(guid != null);
}),
This helpers ensure the correct closing of channels, whithout disposing it. You will be able to see the actual exception then. Edit your post when you'll find the actual exception.
(Maybe your service factory is already using this technique. If not, do not hesitate to update it like my class).
[edit] You still have to play with config. here is a probably working config for you :
contract="The.Correct.Namespace.IFxCurveService"
name="IFxCurveService" />
I am using 4.0 and i have configured it like as follow:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="defaultBasicHttpBinding">
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None"/>
<!--<message clientCredentialType="Certificate" algorithmSuite="Default" />-->
</security>
</binding>
</webHttpBinding>
</bindings>
<client>
<endpoint address="https://abc1234.abc.nsroot.net/MyService/MyService.svc"
binding="webHttpBinding"
bindingConfiguration="defaultBasicHttpBinding"
contract="IMyService"
name="TestJeph"/>
</client>
</system.serviceModel>
FYI: I am calling the WCF Rest service, does it matter?
the following is my service interface that i have created in the web app solution:
namespace anothertest
{
using System;
using System.ServiceModel;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IMyService")]
public interface IMyService
{
[OperationContract]
test.WebService.Entity.BusinessEntity[] GetAllActiveBusiness();
}
public class ProductClient : ClientBase<IMyService>, IMyService
{
#region Members
public test.WebService.Entity.BusinessEntity[] GetAllActiveBusiness()
{
return Channel.GetAllActiveBusiness();
}
#endregion
}
}
the following is the code to call the service:
anothertest.Utility.ServiceHelper.UsingContract<anothertest.IMyService>
("TestJeph",
svc=>
{
string test = svc.UpdateCMPStatus("test", "me");
});
Are you targeting .Net 3.5 or .Net 4.0 ? In .Net 4.0 you get lots of defaults for the configuration of the service. In .Net 3.5 you are going to have to configure the endpoint completely in either in the App.config or programatically. For example what binding is the enpoint using ? If you are using .Net 4.0 then you are going to get a BasicHttpBinding by default since you specified an http uri. In .Net 3.5 you are going to fault as there will be no binding configured.
We are consuming an ASMX service by adding a WCF "Service Reference" to our project. When we do this, by default it is supposed to use the DataContractSerializer and if something goes wrong, it will fall back to the XmlSerializer.
I've tried forcing the DataContractSerializer when generating the proxy classes, but when I do that, they are incomplete and missing all of the custom classes used by the webservice (leaving only the interface for the Soap, SoapChannel, and the SoapClient class).
Well, something is going wrong and it is falling back to the use the XmlSerializer. I do not see any errors or warnings when I generate the reference.
How can I find out what is causing the DataContractSerializer to fail and fall back to the XmlSerializer?
Long story short is that we were unable to force VS to use the DataContractSerializer. Instead we ended up writing our own WCF Service Contracts that represented the webservice. When we consume the service we are instead creating the ChannelFactory generically by using our OWN Service Contracts. Below is the code that we used to create the channel.
/// <summary>
/// A generic webservice client that uses BasicHttpBinding
/// </summary>
/// <remarks>Adopted from: http://blog.bodurov.com/Create-a-WCF-Client-for-ASMX-Web-Service-Without-Using-Web-Proxy/
/// </remarks>
/// <typeparam name="T"></typeparam>
public class WebServiceClient<T> : IDisposable
{
private readonly T channel;
private readonly IClientChannel clientChannel;
/// <summary>
/// Use action to change some of the connection properties before creating the channel
/// </summary>
public WebServiceClient(string endpointUrl, string bindingConfigurationName)
{
BasicHttpBinding binding = new BasicHttpBinding(bindingConfigurationName);
EndpointAddress address = new EndpointAddress(endpointUrl);
ChannelFactory<T> factory = new ChannelFactory<T>(binding, address);
this.clientChannel = (IClientChannel)factory.CreateChannel();
this.channel = (T)this.clientChannel;
}
/// <summary>
/// Use this property to call service methods
/// </summary>
public T Channel
{
get { return this.channel; }
}
/// <summary>
/// Use this porperty when working with Session or Cookies
/// </summary>
public IClientChannel ClientChannel
{
get { return this.clientChannel; }
}
public void Dispose()
{
this.clientChannel.Dispose();
}
}