BizTalk ESB Portal - Exception Handling - biztalk

I am reasonably experienced in BizTalk but new to the ESB Tool kit. We don't really have the need for an ESB solution as such but I would like to use the ESB Portal to display errors, modify messages and resubmit.
I have successfully, as far as I can tell, installed and configured the ESB tool kit correctly on my dev machine.
I have managed to send errors to the portal by enabling routing for failed messages and from within an Orchestration by creating a message thus: FaultMessage = Microsoft.Practices.ESB.ExceptionHandling.ExceptionMgmt.CreateFaultMessage();
The messages display correctly in the portal and on selecting 'Edit' I am given the option to resubmit via WCF OnRamp, SOAP OnRamp and HTTPReceive. This is where my problem starts. I have been using the WCF OnRamp to resubmit and on doing so I get a message:
This message has been successfully resubmitted
However on returning to the home screen of the portal I now have a new error for the Microsoft.Practices.ESB application:
There was a failure executing the receive pipeline: "Microsoft.Practices.ESB.Itinerary.Pipelines.ItinerarySelectReceiveXml, Microsoft.Practices.ESB.Itinerary.Pipelines, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Source: "ESB Itinerary Selector" Receive Port: "OnRamp.Itinerary" URI: "/ESB.ItineraryServices.WCF/ProcessItinerary.svc" Reason: Error 135008: The itinerary was not found in the repository.
I presume I need to configure something here, a resolver perhaps for my message but I, so far, have not been able to find a guide that will help me through this issue. Is there a walk through out there some where that shows the full end to end exception handling with ESB Portal? I have managed to find plenty of help with getting messages into it but not for configuring for resubmit. Thanks.

Coincidentally I was trying to make this work today as well...
If you set the Itinerary resolver connection string on the WCF OnRamp's receive pipeline component configuration to use ITINERARY-STATIC:\headerRequired=true; (instead of ITINERARY-STATIC:\headerRequired=false;), then you'll get the following message in the event viewer:
The itinerary name is required and was not provided
Meaning the Itinerary isn't present in the custom SOAP header.
I also traced the message going from the ESB.Portal using Fiddler (after turning off the Message security in both the ESB.Portal and the BizTalk receive location). No Itinerary custom SOAP header.
After going through the ESB.Portal code, I found the cause in MessageResubmitter.cs:
[Serializable]
public static class MessageResubmitter
{
/// <summary>
/// Submits an XML message to the WCF OnRamp. The URL of the WCF OnRamp is defined in the
/// portal web.config. Context properties are not resubmitted, they are expected to be
/// applied by the receiving pipeline.
/// </summary>
/// <param name="doc">The XML document to submit.</param>
/// <returns>True if the submission was successful, false if the submission failed.</returns>
public static bool ResubmitWCF(XmlDocument doc)
{
try
{
ProcessRequestClient onRamp = new ProcessRequestClient();
onRamp.SubmitRequest(**null**, doc.OuterXml);
return true;
}
catch (Exception)
{
return false;
}
}
The first argument of SubmitRequest is the Itinerary, which is set to null. This means the ESB.Portal does not resend the Itinerary as a custom SOAP header to BizTalk when you resubmit the message.
At the moment, I can think of the following options to make this work:
1) Create a (or modify the existing) generic WCF OnRamp to use the BRE to determine the Itinerary to be associated with the resubmitted message. This could however become complex, because you'll need to create your rules to be able to deal with any messages resubmitted from any step within your itineraries.
2) Modify the code of the ESB.Portal to be able to resend the Itinerary + current step as a Custom SOAP header.
I'm probably going for option 2.

The WCF OnRamp uses the ItinerarySelectReceiveXml pipeline this can be configured to point to an Itinerary or Business Rule and thus the message can be easily routed depending on its message type and content.
My issue now is that a third party got there before me on our installation so I am now looking into creating a new OnRamp and configuring the ESB portal to pick that up in its resubmit list.

We had a similar issue recently. While we were exporting our itineraries to a local database, and deploying them, the ESB would not be able to find the itineraries.
It turned out a consultant we had on site had modified the esb.config file in ESB Toolkit to look for itineraries on a server instead of the local machine.
So, if, like me, you are sure the itineraries are being exported to the right place and that they are deployed, modify the esb.config connection string.
<connectionStrings>
<add name="ItineraryDb" connectionString="Data Source=.;Initial Catalog=EsbItineraryDb;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionString>

Related

Call a web service from .net code

I'm trying to create an application in Visual Studio 2015 that calls a web service set up on one of our servers (It's a third party web service so I don't really know anything about its implementation).
I can browse to the web service definition, and view the wsdl and singleWsdl definitions.
In Visual Studio, I right-click on the Service References folder and choose 'Add Service Reference...', type in the URL to the web service (I've also tried the wsdl and singleWsdl URLs which produce the same result) and click 'Go'. The operations are shown, so I enter a namespace and click 'OK'.
At this point lots of code is generated, but, and I think this is the cause of my problem, there are no changes made to the config file.
When I try and run my code I get an error:
"An exception of type 'System.InvalidOperationException' occurred in
System.ServiceModel.dll but was not handled in user code
Additional information: Could not find default endpoint element that
references contract 'CRMContactEventService.ContactEventService' in
the ServiceModel client configuration section. This might be because
no configuration file was found for your application, or because no
endpoint element matching this contract could be found in the client
element."
I've tried using SvcUtil.exe to generate the proxy but it also does not generate any config file. I've tried using wsdl.exe to generate another wsdl file but that just generates an empty file. I've tried saving the singleWsdl file to my local drive and using that instead of the URL but the results are the same.
Now after lots of searching, I have tried to manually add what I think is missing to the web.config file:
<system.serviceModel>
<client>
<endpoint address="http://...<url to service>.../ContactEventService.svc"
binding ="netHttpBinding"
contract = "CRMContactEventService.ContactEventService" />
</client>
</system.serviceModel>
This changes the error message:
"An exception of type 'System.ServiceModel.EndpointNotFoundException'
occurred in mscorlib.dll but was not handled in user code
Additional information: There was no endpoint listening at
http://....../ContactEventService.svc that could
accept the message. This is often caused by an incorrect address or
SOAP action. See InnerException, if present, for more details."
InnerException contains:
"The remote server returned an error: (404) Not Found."
I have tried several different values for the binding parameter in the web.config file as I don't know which one to use, but most return the above error or some other error which makes it clear that I made a bad choice.
I've been looking at this problem for far too long and I cannot see how to get this working, or even whether it's a problem with what I am doing or a problem with the web service.
Can you first manually make the SOAP call to the actual service endpoint and check if you're getting the expected result? The free SoapUI client is very useful for this type of troubleshooting. (Having said that, if the remote endpoint is responding correctly, I think you're on the right path to figuring this out.)

ASP.NET DbSet Join

I have two classes, client and user. User is a field in client (there is a foreign key). I am trying to do a join, getting all the clients and the related user (it is one to one).
I am using Entity Framework and a web service that gives me my data.
I currently am getting all my clients like:
public DbSet<Client> getClients()
{
return context.Clients;
}
I need to also get the related object user. I found an example that tells me to do:
public DbSet<Client> getClients()
{
return context.Clients.include(x => x.User);
}
This throws an exception, I need to be working with IQueryable. If I change my function the connection to the web service does not work.
How do I do what I am trying to do?
EDIT:
The exception I get from the webservice is An error occurred while receiving the HTTP response to http://localhost:60148/WebService.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.
try these links:
Expose IQueryable Over WCF Service
and IQueryable problems using WCF you need to send something else back, like a List, as IQueryable is actually a query, which you can't send back. the links above provide alternative ways to do it, to get around the IQueryable restriction.

Isolating specific browser instance with SignalR

We are building an app which will send messages to the browser using SignalR. The user may have multiple browser instances open and we would like each message to be sent to the appropriate browser. Our understanding is that the ClientId ConnectionId would allow us to do this. The issue we're running into is accessing the ClientId ConnectionId, or SessionId, at the appropriate times in the codebase. Here's our scenario:
A MVC Action executes and, as part of that processing, a call to a Biztalk endpoint is made. The Biztalk execution is out of process (from the point of view of the MVC Action) and doesn't return when completed. This is by design. To notify the MVC application that it has completed, Biztalk sends a message to the MVC application's SignalR hub by calling the /myapp/signalr endpoint. The message is received by SignalR and then should be routed to the appropriate browser instance.
Since the message to SignalR is being sent by Biztalk, and not the MVC application, the ClientId of the connection to SignalR is not the one that identifies the browser instance that should receive the message. So what we are attempting to implement is somethign similar to the Return Address pattern by including the ClientId ConnectionId of the browser instance that initiates the Biztalk call in the message to Biztalk. When Biztalk sends its message to SignalR one of the contents is that original ClientId ConnectionId value. When SignalR processes the message from Biztalk it then can use the ClientId ConnectionId included in the message to route that message to the appropriate browser instance. (Yes we know that this won't work if the browser has been closed and re-opened and we're fine with that.)
The problem we face is that when initially sending the message to Biztalk from our MVC Action we cannot access the ClientId ConnectionId as it's only available in the Hub's Context. This is understandable since the MVC Action doesn't know which Hub context to look for.
What we have tried in it's place is to pass the SessionId through the Biztalk message and return it to SignalR. This solves the problem of including the browser instance identifier in the Biztalk message and returning it to SignalR. What it introduces is the fact that when a client connects to the Hub we cannot access the Session (and thus the SessionId) in the Hub's OnConnect method.
David Fowler posted a gist that reportedly shows how to make readonly SessionState accessible in a Hub but it doesn't work. (https://gist.github.com/davidfowl/4692934) As soon as we add this code into our application messages sent to SignalR cause a HTTP 500 error which is caused by SignalR throwing the following exception.
[ArgumentNullException: Value cannot be null.Parameter name: s]
System.IO.StringReader..ctor(String s) +10688601
Microsoft.AspNet.SignalR.Json.JsonNetSerializer.Parse(String json, Type targetType) +77
Microsoft.AspNet.SignalR.Json.JsonSerializerExtensions.Parse(IJsonSerializer serializer, String json) +184
Microsoft.AspNet.SignalR.Hubs.HubRequestParser.Parse(String data) +101
Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(IRequest request, String connectionId, String data) +143
Microsoft.AspNet.SignalR.<>c__DisplayClassc.<ProcessRequest>b__7() +96
Microsoft.AspNet.SignalR.<>c__DisplayClass3c.<FromMethod>b__3b() +41
Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(Func`1 func) +67
No matter the mode that we set SessionStateBehavior (as shown by David Fowler's gist) we either get this exception when sending a message to the Hub or SessionState is null when we are in the Hub's OnConnect.
So, after all that pre-amble, what we are asking is how do people update the appropriate client when working with this type of disconnected messaging in SignalR?
If you're looking to send data to clients outside of a normal request to a hub then I'd recommend having a static Concurrent Dictionary on your hub that manages your users and maps them to corresponding connection Id's.
With this approach you can send to any user at any point based on their mapped Connection Id. Therefore when sending your data to Biztalk all you need to do is send your user id (created by you) and then when the data flows back to SignalR you can lookup the ConnectionId (if one exists) for that given user id.
Lastly, you can manage your user mappings by adding users to your concurrent dictionary in OnConnected, adding only if they are not there in OnReconnected, and removing in OnDisconnected.

Thinktecture.IdentityModel with SimpleMembership

I want to use ASP.NET SimpleMembership to authenticate users that consume my WebAPI. Thinktecture has a wonderful authentication library called Thinktecture.IdentityModel (http://thinktecture.github.com/Thinktecture.IdentityModel.45/) with an example that ties Forms Auth with Basic Auth (source). However, the example uses Membership.ValidateUser() which doesn't work without a ASP.NET Membership provider, which isn't supported by SimpleMembership (source) (edit: this isn't entirely true, see Mark's answer below).
Edit:
Here's what I did:
1) Create a new MVC Internet Application
2) Install Thinktecture.IdentityModel via NuGet
3) Create a model and an api controller via scaffolding:
public class Goober
{
public int GooberId { get; set; }
public string GooberWords { get; set; }
}
4) Ran the project, created a new user and created a new Goober using Fiddler
5) Added [Authorize] to GetGoober(int id)
6) In WebApiConfig.cs added:
var authConfig = new AuthenticationConfiguration();
authConfig.AddBasicAuthentication((userName, password) =>
Membership.ValidateUser(userName, password));
config.MessageHandlers.Add(new AuthenticationHandler(authConfig));
When I run the project and hit api/goober/1 with Fiddler I get a 401 www-Authenticate: unspecified. But if I log in first using the AccountController then use Fiddler I get a 200 and everything is peachy.
Edit
Okay, I think the problem isn't related to my initial question. I suspect it's related to the initialization of SimpleMembership in the template. When I open the project and run debug then hit the api with Fiddler I can't get past Auth. But when I simply click the "register" link on the web frontend I get past Auth. I'm guessing it's because the InitializeSimpleMembershipAttribute is called at the AccountController so doesn't initialize until the controller is called?
I've tried using WebSecurity.Login() in the place of Membership.ValidateUser() but that doesn't work.
I'm at a loss on how to actually implement this. Does anyone have any advice? Or maybe I'm attempting to tackle this problem from the wrong angle?
You are correct that ASP.NET Membership provider is not compatible with the SimpleMembershipProvider however SimpleMembershipProvider does support ValidateUser, see here. Assuming SimpleMembership is correctly configured and initalised you should still be able to call Membership.ValidateUser().
If you have already tried Membership.ValidateUser() and got an error please let me know and we can try resolve it.
Update
So having followed your reproduction steps I have managed to pin-point an error. Having brought the Thinktecture AuthenticationHandler handler inline and run in debug. 30 seconds after a request to the api controller a database connection error is being raised. This is failing asynchronously and silently.
After some fiddling around I believe it is the DefaultConnection connection string which is at fault.
Like me your default connection probably contains a file name like this:
AttachDBFilename=|DataDirectory|\aspnet-MvcApplication3-20121215104323.mdf"
When ValidateUser is called inside the delegate registered at app start up (for validating the credentials) it appears to be failing to resolve |DataDirectory| I found that by updating this path to the full name my connection problems went away.
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MvcApplication3-20121215104323;Integrated Security=SSPI;AttachDBFilename=C:\mydatabase\file\path\example\aspnet-MvcApplication-20121215104323.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>
I then found this post here, it indicates that the AppDomain has not had it's data directory set correctly at this point.
So once the config set up and the connection string altered with a proper file path and a user name "test" and a password "testing" this request through fiddler got a 200:
GET http://localhost/api/goober/1
User-Agent: Fiddler
Host: localhost
Authorization: Basic dGVzdDp0ZXN0aW5n
As an aside
I found that to get the forms authorisation token to also allow access to the api controllers I had to add this. Otherwise the Thinkteckture code sets the principle back to anonymous:
Add this
authConfig.InheritHostClientIdentity = true;
To counteract this (line 52):
if (_authN.Configuration.InheritHostClientIdentity == false)
{
//Tracing.Information(Area.HttpAuthentication, "Setting anonymous principal");
SetPrincipal(Principal.Anonymous);
}

How do I modify my WCF service to work with ASP.NET pages?

I created a WCF service (.NET 3.5) that grabs data from a db and returns a list of objects. It works just fine. I tested it using the WCFTestClient application and got the desired results.
Now, I tried to create an ASP.NET web application and consume the service. After enabling <serviceDebug includeExceptionDetailInFaults="true"/> in the config file, the error message is "Object reference not set to an instance of an object."
How do I modify the service to work with ASP.NET?
Thanks!
Update 1 - I created a reference to the service. When I ran my very basic testing app, based on the sample code displayed on the service's default web page, I got the "Object reference not set to an instance of an object" message.
The service is returning the correct results when I use the WCF Test Client utility provided with Visual Studio. When I try to use the following code, all in the page_load event, I get the object reference message.
PhoneNumberServiceClient client = new PhoneNumberServiceClient();
// Use the 'client' variable to call operations on the service
List<PhoneNumber> phones = client.GetPhoneNumbers();
// Always close the client.
client.Close();
I will try to get some additional debug info.
Have you tried openning the service after you initialize it by using:
client.Open();
If the problem still persists maybe the it lies on the WCF method itself. Try to add breakpoints on your method and debug it to trace the cause of the error. You could also use profilers.
You also have to configure the Service to allow exception details in results. Follow the info in here to make that happen.
I presume your ASP.NET app is expecting to catch certain information from the service that you have not set it up to provide, hence the null object error.
I would have written this as comment, but couldn't figure out how to, so I guess I'll attempt an answer.
You mentionned you used the built in test client, but when You created your client application(the website that you want to be calling your new service with) have you configured it's end point(web.config) to match the end of your wcf service? For example, a client app calling a wcf service with wsHttpBinding endpoint:
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:8080/ContractorService" binding="wsHttpBinding" contract="ContractorService.IContractorService"
name="ContractorService_WsHttp">
</endpoint>
</client>
</system.serviceModel>
Also, your service is returning a list, the default service reference that you added to your client very likely stops at array. Right click the service reference your client now has and select configure service reference, in the drop down box for collection type, instead of System.array, select System.Collections.Generic.List.
If you need details on configuration, check here http://msdn.microsoft.com/en-us/library/dd936243.aspx
Place a break point in the Page_Load method of your client, you should be able to step through your client code and it will jump in automatically in your wcf service once you call it's method, then you can see exactly where it fails.
Hope this helps

Resources