public static MessageFilterTable <IEnumerable <ServiceEndpoint> > CreateFilterTable(string name) { if (string.IsNullOrEmpty(name)) { throw FxTrace.Exception.ArgumentNullOrEmpty("name"); } RoutingSection routingSection = (RoutingSection)ConfigurationManager.GetSection("system.serviceModel/routing"); if (routingSection == null) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR2.RoutingSectionNotFound)); } FilterTableEntryCollection routingTableElement = routingSection.FilterTables[name]; if (routingTableElement == null) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR2.RoutingTableNotFound(name))); } XmlNamespaceManager xmlNamespaces = new XPathMessageContext(); foreach (NamespaceElement nsElement in routingSection.NamespaceTable) { xmlNamespaces.AddNamespace(nsElement.Prefix, nsElement.Namespace); } FilterElementCollection filterElements = routingSection.Filters; MessageFilterTable <IEnumerable <ServiceEndpoint> > routingTable = new MessageFilterTable <IEnumerable <ServiceEndpoint> >(); foreach (FilterTableEntryElement entry in routingTableElement) { FilterElement filterElement = filterElements[entry.FilterName]; if (filterElement == null) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR2.FilterElementNotFound(entry.FilterName))); } MessageFilter filter = filterElement.CreateFilter(xmlNamespaces, filterElements); //retreive alternate service endpoints IList <ServiceEndpoint> endpoints = new List <ServiceEndpoint>(); if (!string.IsNullOrEmpty(entry.BackupList)) { BackupEndpointCollection alternateEndpointListElement = routingSection.BackupLists[entry.BackupList]; if (alternateEndpointListElement == null) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR2.BackupListNotFound(entry.BackupList))); } endpoints = alternateEndpointListElement.CreateAlternateEndpoints(); } //add first endpoint to beginning of list endpoints.Insert(0, ClientEndpointLoader.LoadEndpoint(entry.EndpointName)); routingTable.Add(filter, endpoints, entry.Priority); } return(routingTable); }
public override object ProvideValue(IServiceProvider serviceProvider) { XPathMessageContext context = new XPathMessageContext(); foreach (KeyValuePair <string, string> pair in this.namespaces) { context.AddNamespace(pair.Key, pair.Value); } return(context); }
public RoutingTable() { this.filterTable = new XPathMessageFilterTable <EndpointAddress>(); this.randomNumberGenerator = new Random(); XmlNamespaceManager manager = new XPathMessageContext(); XmlReader routingTableDataFileReader = XmlReader.Create(ConfigurationManager.AppSettings["routingTableXmlFile"]); RoutingTableData routingTableData = (RoutingTableData) new XmlSerializer(typeof(RoutingTableData)).Deserialize(routingTableDataFileReader); foreach (RouterNamespace ns in routingTableData.RouterNamespacesSection.RouterNamespaces) { manager.AddNamespace(ns.Prefix, ns.NamespaceUri); } foreach (Route route in routingTableData.RoutesSection.Routes) { this.filterTable.Add(new XPathMessageFilter(route.XPath, manager), new EndpointAddress(route.Uri)); } }
private static void ConfigureRouterViaCode(ServiceHost serviceHost) { //This code sets up the Routing Sample via code. Rename the provided app.config //to App.config.example and uncomment this method call to run a config-based Routing Service //set up some communication defaults //note that some of these are a little artifical for the purpose of demonstrating //different filter types and how to define them //the regular calculator service is located at net.tcp://localhost:9090/servicemodelsamples/service/ string calcDestinationAddress = "net.tcp://localhost:9090/servicemodelsamples/service/"; //the rounding calc service is located at net.tcp://localhost:8080/servicemodelsamples/service/ string roundingDestinationAddress = "net.tcp://localhost:8080/servicemodelsamples/service/"; //the "Default" router address string routerAddress = "http://localhost/routingservice/router/general"; //the virtualized address of the regular calculator string virtualCalculatorAddress = "http://localhost/routingservice/router/regular/calculator"; //the virtualized address of the rounding calculator string virtualRoundingCalculatorAddress = "http://localhost/routingservice/router/rounding/calculator"; //set up the bindings for the Routing Service's communication with the client Binding routerBinding = new WSHttpBinding(); //set up the bindings for the Routing Service's communication with the Calculator Services Binding clientBinding = new NetTcpBinding(); //use the IRequestReplyRouter since the client and services are expecting request/response communication ContractDescription contract = ContractDescription.GetContract(typeof(IRequestReplyRouter)); //set up the default Router endpoint ServiceEndpoint routerEndpoint = new ServiceEndpoint(contract, routerBinding, new EndpointAddress(routerAddress)); routerEndpoint.Name = "routerEndpoint"; //create the virtual endpoint for the regular CalculatorSerivice ServiceEndpoint calcEndpoint = new ServiceEndpoint(contract, routerBinding, new EndpointAddress(virtualCalculatorAddress)); calcEndpoint.Name = "calculatorEndpoint"; //create the virtual endpoint for the rounding CalculatorSerivice ServiceEndpoint roundingEndpoint = new ServiceEndpoint(contract, routerBinding, new EndpointAddress(virtualRoundingCalculatorAddress)); roundingEndpoint.Name = "roundingEndpoint"; //add the inbound endpoints that the Routing Service will listen for serviceHost.AddServiceEndpoint(routerEndpoint); serviceHost.AddServiceEndpoint(calcEndpoint); serviceHost.AddServiceEndpoint(roundingEndpoint); //create the client endpoints the router will route messages to ServiceEndpoint RegularCalcEndpoint = new ServiceEndpoint(contract, new NetTcpBinding(), new EndpointAddress(calcDestinationAddress)); ServiceEndpoint RoundingCalcEndpoint = new ServiceEndpoint(contract, new NetTcpBinding(), new EndpointAddress(roundingDestinationAddress)); //create the endpoint lists that contains the service endpoints we want to route to List <ServiceEndpoint> RegularCalcs = new List <ServiceEndpoint>(); List <ServiceEndpoint> RoundingCalcs = new List <ServiceEndpoint>(); //add the endpoints in the order we want the Routing Service to try sending to them RegularCalcs.Add(RegularCalcEndpoint); RoundingCalcs.Add(RoundingCalcEndpoint); //create the default RoutingConfiguration RoutingConfiguration rc = new RoutingConfiguration(); //create all of the necessary filters //create a new XPathMessageFilter that will look for the custom header //Unfortunately, the default namespace manager doesn't have the custom namespace // that we use defined so we have to define that prefix ourselves. //Any message that shows up with this header will match this filter. XPathMessageContext namespaceManager = new XPathMessageContext(); namespaceManager.AddNamespace("custom", "http://my.custom.namespace/"); XPathMessageFilter xpathFilter = new XPathMessageFilter("sm:header()/custom:RoundingCalculator = 1", namespaceManager); //create a new Endpoint Name Message Filter, which will match any message that was received //on the calculator Endpoint. The Endpoint name was defined when we created the service endpoint object EndpointNameMessageFilter endpointNameFilter = new EndpointNameMessageFilter("calculatorEndpoint"); //Create a new Prefix Endpoint Address Message Filter. This will match any message that showed up on an endpoint //with an address that matches the address -prefix- (or front portion) provided. In this example we define //the address prefix as "http://localhost/routingservice/router/rounding/". This means that any messages that arrive //addressed to http://localhost/routingservice/router/rounding/* will be matched by this filter. In this case, that //will be messages that show up on the rounding calculator endpoint, which has the address of //http://localhost/routingservice/router/rounding/calculator. PrefixEndpointAddressMessageFilter prefixAddressFilter = new PrefixEndpointAddressMessageFilter(new EndpointAddress("http://localhost/routingservice/router/rounding/")); //create two new Custom message filters. In this example, we're going to use a "RoundRobin" message filter //this message filter is created in the provided RoundRobinMessageFilter.cs file. These filters, when set //to the same group, will alternate between reporting that they match the message and that they don't, such that //only one of them will respond true at a time. RoundRobinMessageFilter roundRobinFilter1 = new RoundRobinMessageFilter("group1"); RoundRobinMessageFilter roundRobinFilter2 = new RoundRobinMessageFilter("group1"); //Now let's add all of those Message Filters to the Message Filter Table //note the use of priorities to influence the order in which the MessageFilter Table //executes the filters. The higher the priority, the sooner the filter will be //executed, the lower the priority, the later a filter will be executed. Thus a filter //at priority 2 runs before a filter at priority 1. The default priority level //if one isn't specified is 0. A Message Filter Table executes all of the filters //at a given priority level before moving to the next lowest priority level. //If a match is found at a particular priority, then the Message Filter Table doesn't //continue trying to find matches at the next lower priority. // //While this example shows how to use Message Filter priorities, in general it is //more performant and better design to design and configure your filters such that they //don't require prioritization in order to function correctly. //The first filter we add is the XPath filter, and we set its priority to 2. //Thus this will be the first MessageFilter that executes. If it finds the custom //header, regardless of what the results of the other filters would be, the message //will be routed to the Rounding Calculator endpoint. //catch messages that showed up with the custom header rc.FilterTable.Add(xpathFilter, RoundingCalcs, 2); //At priority 1, we'll add two filters. These will only run if the xpath filter //at priority 2 doesn't match the message. These two filters show two different ways to //determine where the message was addressed when it showed up. Because they effectively check //to see if the message arrived at one of the two endpoints, we can run them //at the same priority level since they're never going to both return true. //find messages that showed up addressed to the specific virtual endpoints rc.FilterTable.Add(endpointNameFilter, RegularCalcs, 1); rc.FilterTable.Add(prefixAddressFilter, RoundingCalcs, 1); //Finally, run the RoundRobin message filters. Since we configured the filters //with the same group name, only one of them will match at a time. Since we've already //Routed all the messages with the custom header, and then those addressed to the specific //virtualized endpoints, these will only be messages that showed up addressed to the //default router endpoint without the custom header. Since these will switch based //on a per message call, half of the operations will go to the regular calculator, and //half will go to the Rounding calculator. rc.FilterTable.Add(roundRobinFilter1, RegularCalcs, 0); rc.FilterTable.Add(roundRobinFilter2, RoundingCalcs, 0); //create the Routing Behavior with the Routing Configuration and add it to the //serviceHost's Description. serviceHost.Description.Behaviors.Add(new RoutingBehavior(rc)); }
private static Activity GetPropertyWorkflow() { // Correlation handle used to link operations together Variable <CorrelationHandle> operationHandle = new Variable <CorrelationHandle>(); // The generated property Id Variable <Guid> propertyId = new Variable <Guid>(); // Variable used to indicate that the workflow should finish Variable <bool> finished = new Variable <bool>("Finished", false); Variable <string> address = new Variable <string>(); Variable <string> owner = new Variable <string>(); Variable <double> askingPrice = new Variable <double>(); // Initial receive - this kicks off the workflow Receive receive = new Receive { CanCreateInstance = true, OperationName = "UploadPropertyInformation", ServiceContractName = XName.Get("IProperty", ns), Content = new ReceiveParametersContent { Parameters = { { "address", new OutArgument <string>(address) }, { "owner", new OutArgument <string>(owner) }, { "askingPrice", new OutArgument <double>(askingPrice) } } } }; // Define the local namespace XPathMessageContext messageContext = new XPathMessageContext(); messageContext.AddNamespace("local", ns); // Extracts the guid sent back to the client on the initial response MessageQuerySet extractGuid = new MessageQuerySet { { "PropertyId", new XPathMessageQuery("sm:body()/ser:guid", messageContext) } }; // Extracts the guid sent up with the property image MessageQuerySet extractGuidFromUploadRoomInformation = new MessageQuerySet { { "PropertyId", new XPathMessageQuery(@"sm:body()/local:UploadRoomInformation/local:propertyId", messageContext) } }; // Receive used to indicate that the upload is complete Receive receiveDetailsComplete = new Receive { OperationName = "DetailsComplete", ServiceContractName = XName.Get("IProperty", ns), CorrelatesWith = operationHandle, CorrelatesOn = extractGuid, Content = ReceiveContent.Create(new OutArgument <Guid>(propertyId)) }; Variable <string> roomName = new Variable <string>(); Variable <double> width = new Variable <double>(); Variable <double> depth = new Variable <double>(); // Receive room information Receive receiveRoomInfo = new Receive { OperationName = "UploadRoomInformation", ServiceContractName = XName.Get("IProperty", ns), CorrelatesWith = operationHandle, CorrelatesOn = extractGuidFromUploadRoomInformation, Content = new ReceiveParametersContent { Parameters = { { "propertyId", new OutArgument <Guid>() }, { "roomName", new OutArgument <string>(roomName) }, { "width", new OutArgument <double>(width) }, { "depth", new OutArgument <double>(depth) }, } } }; return(new Sequence { Variables = { propertyId, operationHandle, finished, address, owner, askingPrice }, Activities = { receive, new WriteLine { Text = "Assigning a unique ID" }, new Assign <Guid> { To = new OutArgument <Guid> (propertyId), Value = new InArgument <Guid> (Guid.NewGuid()) }, new WriteLine { Text = new InArgument <string> (env => string.Format("{0} is selling {1} for {2}.\r\nAssigned unique id {3}.",owner.Get(env), address.Get(env), askingPrice.Get(env), propertyId.Get(env))) }, new SendReply { Request = receive, Content = SendContent.Create(new InArgument <Guid> (env => propertyId.Get(env))), CorrelationInitializers = { new QueryCorrelationInitializer { CorrelationHandle = operationHandle, MessageQuerySet = extractGuid } } }, new While { Condition = ExpressionServices.Convert <bool>(env => !finished.Get(env)), Body = new Pick { Branches = { new PickBranch { Variables = { roomName, width,depth }, Trigger = receiveRoomInfo, Action = new WriteLine{ Text = new InArgument <string> (env => string.Format("Room '{0}' uploaded, dimensions {1}W x {2}D",roomName.Get(env), width.Get(env), depth.Get(env))) }, }, new PickBranch { Trigger = receiveDetailsComplete, Action = new Sequence { Activities = { new Assign <bool> { To = new OutArgument <bool>(finished), Value = new InArgument <bool>(true) }, new WriteLine { Text = "Property Details Complete" } } } } } } }, new WriteLine { Text = "Finished!" } } }); }
static WorkflowService GetService() { Variable <Customer> customer = new Variable <Customer>(); Variable <Order> order = new Variable <Order>(); Variable <string> drug = new Variable <string>(); Variable <double> adjustedCost = new Variable <double>(); Variable <int> percentagePaidByInsurance = new Variable <int>(); Variable <CorrelationHandle> customerHandle = new Variable <CorrelationHandle>(); Variable <CorrelationHandle> orderHandle = new Variable <CorrelationHandle>(); XPathMessageContext pathContext = new XPathMessageContext(); pathContext.AddNamespace("psns", Constants.PharmacyServiceNamespace); // <Snippet2> MessageQuerySet GetOrderQuerySet = new MessageQuerySet { { "OrderID", new XPathMessageQuery("//psns:Order/psns:OrderID", pathContext) } }; // </Snippet2> MessageQuerySet GetOrderIDQuerySet = new MessageQuerySet { { "OrderID", new XPathMessageQuery("//ser:guid", pathContext) } }; MessageQuerySet customerQuerySet = new MessageQuerySet { { "CustomerID", new XPathMessageQuery("//psns:GetBaseCost/psns:Customer/psns:CustomerID", pathContext) } }; MessageQuerySet customerIDQuerySet = new MessageQuerySet { { "CustomerID", new XPathMessageQuery("//ser:guid", pathContext) } }; // This will use implicit correlation within the workflow using the WorkflowServiceHost's default CorrelationHandle // <Snippet3> Receive prescriptionRequest = new Receive { DisplayName = "Request Perscription", OperationName = "GetBaseCost", ServiceContractName = Constants.PharmacyServiceContractName, CanCreateInstance = true, //CorrelatesWith = customerHandle, -- add this line for explicit correlation CorrelatesOn = customerQuerySet, Content = new ReceiveParametersContent { Parameters = { { "Customer", new OutArgument <Customer>(customer) }, { "Drug", new OutArgument <string>(drug) }, } } }; // </Snippet3> // This will use implicit correlation within the workflow using the WorkflowServiceHost's default CorrelationHandle Receive GetInsurancePaymentPercentageRequest = new Receive { DisplayName = "Get Insurance Coverage", ServiceContractName = Constants.PharmacyServiceContractName, OperationName = "GetInsurancePaymentPercentage", CanCreateInstance = true, //CorrelatesWith = customerHandle, -- add this line for explicit correlation CorrelatesOn = customerIDQuerySet, Content = ReceiveContent.Create(new OutArgument <Guid>()) }; // This will explicitly correlate with the SendReply action after the prescriptionRequest using the OrderID (stored in the orderHandle) Receive GetAdjustedCostRequest = new Receive { DisplayName = "Get Adjusted Cost", OperationName = "GetAdjustedCost", ServiceContractName = Constants.PharmacyServiceContractName, CanCreateInstance = true, CorrelatesOn = GetOrderIDQuerySet, CorrelatesWith = orderHandle, Content = ReceiveContent.Create(new OutArgument <Guid>()) }; Activity PrescriptonWorkflow = new Sequence() { Variables = { customer, order, drug, percentagePaidByInsurance, adjustedCost, customerHandle, orderHandle }, Activities = { new WriteLine { Text = "Beginning Workflow" }, new Parallel { Branches = { new Sequence { Activities = { GetInsurancePaymentPercentageRequest, new Assign <int> { To = new OutArgument <int>((e) => percentagePaidByInsurance.Get(e)), Value = new InArgument <int>((e) => new Random().Next(0, 100)) }, new SendReply { DisplayName = "Return Percentage", Request = GetInsurancePaymentPercentageRequest, Content = SendContent.Create(new InArgument <int>((e) => percentagePaidByInsurance.Get(e))) } } }, new Sequence { Activities = { prescriptionRequest, new WriteLine { Text = new InArgument <string>(env => (string.Format("{0}, {1}\t{2}",customer.Get(env).LastName, customer.Get(env).FirstName, customer.Get(env).CustomerID.ToString()))) }, new Assign <Order> { To = new OutArgument <Order>(order), Value = new InArgument <Order>((e) => new Order() { CustomerID = customer.Get(e).CustomerID,Drug = drug.Get(e), OrderID = Guid.NewGuid() }) }, new WriteLine { Text = new InArgument <string>(env => (string.Format("OrderID: {0}",order.Get(env).OrderID.ToString()))) }, new Assign <int> { To = new OutArgument <int>((e) => order.Get(e).Cost), Value = new InArgument <int>((e) => new Random().Next(20, 50)) }, // <Snippet0> new SendReply { DisplayName = "Send Adjusted Cost", Request = prescriptionRequest, // Initialize the orderHandle using the MessageQuerySet to correlate with the final GetAdjustedCost request CorrelationInitializers = { // <Snippet1> new QueryCorrelationInitializer { CorrelationHandle = orderHandle, MessageQuerySet = GetOrderQuerySet } // </Snippet1> }, Content = SendContent.Create(new InArgument <Order>((e) => order.Get(e))) } // </Snippet0> } } } }, new Assign <double> { To = new OutArgument <double>((e) => adjustedCost.Get(e)), Value = new InArgument <double>((e) => order.Get(e).Cost *(100 - percentagePaidByInsurance.Get(e)) * .01) }, new WriteLine { Text = new InArgument <string>(env => (string.Format("Base Cost: ${0}", order.Get(env).Cost.ToString()))) }, new WriteLine { Text = new InArgument <string>(env => (string.Format("Insurance Coverage: {0}%", percentagePaidByInsurance.Get(env).ToString()))) }, new WriteLine { Text = new InArgument <string>(env => (string.Format("Adjusted Cost: ${0}", decimal.Round(Convert.ToDecimal(adjustedCost.Get(env)), 2)))) }, GetAdjustedCostRequest, new SendReply { Request = GetAdjustedCostRequest, Content = SendContent.Create(new InArgument <double>((e) => adjustedCost.Get(e))) }, new WriteLine { Text = "Workflow Completed" } } }; WorkflowService service = new WorkflowService { Name = "PharmacyService", Body = PrescriptonWorkflow, ConfigurationName = "PharmacyService" }; return(service); }
void OnTypeSelectionChanged(object sender, RoutedEventArgs e) { var contentCorrelationDesigner = (ContentCorrelationTypeExpander)sender; //is selection valid (valid type or property) if (contentCorrelationDesigner.IsSelectionValid) { var path = contentCorrelationDesigner.GetMemberPath(); var type = contentCorrelationDesigner.GetSelectedType(); try { XmlNamespaceManager namespaceManager = null; string xpathQuery = string.Empty; var content = this.Activity.Properties["Content"].Value; if (content.IsAssignableFrom <ReceiveMessageContent>() || content.IsAssignableFrom <SendMessageContent>()) { //generating xpath for message content xpathQuery = XPathQueryGenerator.CreateFromDataContractSerializer(type, path, out namespaceManager); } else { //generating xpath for parameter content XName serviceContractName = null; string operationName = null; string parameterName = contentCorrelationDesigner.SelectedTypeEntry.Name; bool isReply = this.Activity.IsAssignableFrom <SendReply>() || this.Activity.IsAssignableFrom <ReceiveReply>(); if (isReply) { operationName = (string)this.Activity.Properties["Request"].Value.Properties["OperationName"].ComputedValue; serviceContractName = (XName)this.Activity.Properties["Request"].Value.Properties["ServiceContractName"].ComputedValue; if (string.IsNullOrEmpty(operationName) || null == serviceContractName) { ModelItem requestDisplayName; this.Activity.TryGetPropertyValue(out requestDisplayName, "Request", "DisplayName"); throw FxTrace.Exception.AsError(new InvalidOperationException( string.Format(CultureInfo.CurrentUICulture, (string)this.FindResource("parametersRequiredText"), requestDisplayName.GetCurrentValue()))); } } else { operationName = (string)this.Activity.Properties["OperationName"].ComputedValue; serviceContractName = (XName)this.Activity.Properties["ServiceContractName"].ComputedValue; if (string.IsNullOrEmpty(operationName) || null == serviceContractName) { throw FxTrace.Exception.AsError(new InvalidOperationException( string.Format(CultureInfo.CurrentUICulture, (string)this.FindResource("parametersRequiredText"), this.Activity.Properties["DisplayName"].ComputedValue))); } } xpathQuery = ParameterXPathQueryGenerator.CreateFromDataContractSerializer(serviceContractName, operationName, parameterName, isReply, type, path, out namespaceManager); } //use CDF api to build a xpath out of type and its properties string xpath = string.Format(CultureInfo.InvariantCulture, "sm:body(){0}", xpathQuery); //get the context //We need to copy over the namespaces from the manager's table 1 by 1. According to MSDN: //If you specify an existing name table, any namespaces in the name table are not automatically added to XmlNamespaceManager. //You must use AddNamespace and RemoveNamespace to add or remove namespaces. XPathMessageContext messageContext = new XPathMessageContext(); foreach (string prefix in namespaceManager) { if (!string.IsNullOrEmpty(prefix) && !messageContext.HasNamespace(prefix) && prefix != "xmlns") { messageContext.AddNamespace(prefix, namespaceManager.LookupNamespace(prefix)); } } var typeEntry = (ExpanderTypeEntry)contentCorrelationDesigner.Tag; //construct xpath XPathMessageQuery query = new XPathMessageQuery(xpath, messageContext); //store the xpath in the Tag property; this combo's selectedValue is bound to i typeEntry.Tag = query; this.SelectedIndex = 0; this.IsDropDownOpen = false; this.Query = query; this.RaiseEvent(new RoutedEventArgs(XPathCreatedEvent, this)); } catch (Exception err) { MessageBox.Show( err.Message, (string)this.Resources["controlTitle"], MessageBoxButton.OK, MessageBoxImage.Error); } } }