Implementing a Custom Scope Ranker

A custom scope provider is a class that implements the Microsoft.ConnectedIndustry.ServiceModel.Discovery.IScopeRanking interface. This interface is defined in the Microsoft.ConnectedIndustry.ServiceModel assembly. For more information about the IScopeRanking interface, see The IScopeProvider Interface.

You implement the RankServiceInstance method to return an ordered list of service endpoints as an array of Microsoft.ConnectedIndustry.ServiceModel.Discovery.EndpointReferenceType objects. For more information about the EndPointReferenceType class, see The EndpointReferenceType Class. The first service endpoint in this array is the preferred service endpoint used by the client proxy. If the connection to this service endpoint fails, the client proxy uses the second item in the array, and so on until there are no more service endpoints available.

The parameter for the RankServiceInstance method is an array of ProbeMatchOp objects that contain the scope and endpoint address of each service instance that matches the scopes provided by the client proxy. (The Discovery Service finds the appropriate scopes and endpoint addresses.) For more information about the ProbeMatchOp class, see The ProbeMatchOp Class.

The next code example shows the default ScopeRanking class provided with DCS. The RankServiceInstance method orders service endpoints beginning with the endpoint that most closely matches the most detailed scope specified by the client application.

For example, a client application provides a context that contains the following property values:

  • Context.Organization.Plant property: ContosoBank
  • Context.Environment property: HeadOffice
  • Context.Organization.Holding property: London
  • Context.Organization.BusinessUnit property: Finance
  • Context.Organization.OrganizationalUnit property: CashOffice

The Discovery Service determines that the following available service endpoints have scopes that meet the client application requirements:

  • ContosoBank at http://www.contoso.com/bankservice.svc
  • ContosoBank:HeadOffice:London at http://www.contoso.com/london/bankservice.svc
  • ContosoBank:HeadOffice:London:Finance:CashOffice at http://www.contoso.com/london/cashoffice/bankservice.svc

The RankServiceInstance method orders the service endpoints in the following sequence:

  1. http://www.contoso.com/london/cashoffice/bankservice.svc
  2. http://www.contoso.com/london/bankservice.svc
  3. http://www.contoso.com/bankservice.svc

                  public class ScopeRanking : IScopeRanking
{
    // Fields
    private char ScopeSeparator = ':';

    // Methods
    public EndpointReferenceType[] RankServiceInstance(ProbeMatchOp[] listProbeMatchOp)
    {
        List<ProbeMatchOp> eprRanked = new List<ProbeMatchOp>();
        foreach (ProbeMatchOp singleProbeMatchOp in listProbeMatchOp)
        {
            int currentScopeDepth = 0;
            if (singleProbeMatchOp.ProbeMatch.Scopes.UriList == null)
            {
                eprRanked.Insert(eprRanked.Count, singleProbeMatchOp);
                break;
            }
            foreach (string singleScope in singleProbeMatchOp.ProbeMatch.Scopes.UriList)
            {
                if (singleScope.Split(new char[] { this.ScopeSeparator }).Length > currentScopeDepth)
                {
                    currentScopeDepth = singleScope.Split(new char[] { this.ScopeSeparator }).Length;
                }
            }
            if (eprRanked.Count == 0)
            {
                eprRanked.Add(singleProbeMatchOp);
            }
            else
            {
                int eprIndex = 0;
                foreach (ProbeMatchOp currentProbeMatchOp in eprRanked)
                {
                    int scopeDepth = currentProbeMatchOp.ProbeMatch.Scopes.UriList[0].Split(new char[] { this.ScopeSeparator }).Length;
                    if (currentScopeDepth > scopeDepth)
                    {
                        break;
                    }
                    eprIndex++;
                }
                if (eprIndex >= eprRanked.Count)
                {
                    eprRanked.Add(singleProbeMatchOp);
                }
                else
                {
                    eprRanked.Insert(eprIndex, singleProbeMatchOp);
                }
            }
        }
        List<EndpointReferenceType> retVal = new List<EndpointReferenceType>();
        foreach (ProbeMatchOp singleProbeMatchOp in eprRanked)
        {
            retVal.Add(singleProbeMatchOp.ProbeMatch.EndpointReference);
        }
        return retVal.ToArray();
    }
}

                

The following code example shows a custom scope provider named MyCustomScopeRanker. It references an instance of a custom context class named ExContextSample, which adds a property named ExProperty to the current context. A client application can add a value to this property and specify it as part of the scope information passed to the Discovery Service. For more information about building a custom context class, see Building a Custom Context Class. In this example, if the scope ranker detects that one of the endpoints in the probeMatches array matches the scope that references the ExContextSample property, then endpoint is moved to the top of the EndpointReferenceType array returned by the RankServiceInstance method, and it will be the first endpoint that the client proxy attempts to use to access the service.


                  namespace DCS.Samples.Extensions
{
    public class MyCustomScopeRanker : IScopeRanking
    {
        public EndpointReferenceType[] RankServiceInstance(ProbeMatchOp[] probeMatches)
        {
            string customScope = string.Empty;
            ExContextSample currentContext = Context.Current as ExContextSample;
            if (currentContext != null)
            {
                customScope = string.Format("{0}:{1}", 
                    currentContext.Organization.Plant, currentContext.ExProperty);
            }

            List<EndpointReferenceType> eprList = new List<EndpointReferenceType>();
            bool moveToTop = false;
            foreach (ProbeMatchOp probeMatch in probeMatches)
            {
                moveToTop = false;
                if (!string.IsNullOrEmpty(customScope))
                {
                    foreach (string scope in probeMatch.ProbeMatch.Scopes.UriList)
                    {
                        if (scope.Equals(customScope))
                        {
                            moveToTop = true;
                            break;

                        }
                    }
                }

                if (moveToTop)
                {
                    //If there is Uri corresponding to custom scope (creating in custom provider),
                    //give 1st preference to that Uri and move it to top of the Uri list
                    eprList.Insert(0,
                        probeMatch.ProbeMatch.EndpointReference);
                }
                else
                {
                    eprList.Add(probeMatch.ProbeMatch.EndpointReference);
                }
            }

            return eprList.ToArray();
        }
    }
}

                
Show: