Pages

Showing posts with label C# code. Show all posts
Showing posts with label C# code. Show all posts

Thursday, August 22, 2013

Keyword Query in SharePoint 2013


With the release of SharePoint 2013, a lot has changed with the way things were being implemented. For instance, till SP 2010, it was possible to use FullTextSQLQuery to query the results obtained from SharePoint Search Crawls. It was much easier to use considering its syntax was similar to SQL.

Now, in SP 2013, it has been deprecated. There is not even a provision to support backward compatibility FullTextSQLQuery on SharePoint 2013 platform. The only place we developers can now resort to is the KeywordQuery. It can be implemented in the pretty much same way as it was implemented on SP 2010.

So, let me take this opportunity to shed some light on Keyword Query and how it can be used to query crawl results.

KeywordQuery supports two syntax for query string formation –
  1. Keyword Query Language (KQL) and
  2. FAST Query Language (FQL)

KQL is the default syntax which can be used. FQL needs to be explicitly enabled to use it. Following is the sample code which showcases both these approaches –

Approach 1 – KeywordQuery using KQL

static void Main(string[] args)
{
SPSite site = new SPSite("url_whose_results_you_wish_to_query");
SearchServiceApplicationProxy proxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.GetContext(site));

using (KeywordQuery keywordQuery = new KeywordQuery(proxy))
   {
    keywordQuery.ResultsProvider = SearchProvider.Default;
    keywordQuery.StartRow = 0;
    keywordQuery.RowLimit = 500;
    keywordQuery.EnableStemming = true;
    keywordQuery.TrimDuplicates = false;
    keywordQuery.AuthenticationType = QueryAuthenticationType.PluggableAuthenticatedQuery;
    keywordQuery.Culture = CultureInfo.CurrentCulture;
    keywordQuery.KeywordInclusion = KeywordInclusion.AllKeywords;
    keywordQuery.QueryText = "scope:\"All Sites\" AND (contentclass:\"STS_Site\" OR contentclass:\"STS_Web\")";
    keywordQuery.SelectProperties.Add("path");
    keywordQuery.SelectProperties.Add("title");
    keywordQuery.SelectProperties.Add("contentclass");
    SearchExecutor executor = new SearchExecutor();
    ResultTableCollection resultTableCollection = executor.ExecuteQuery(keywordQuery);
    var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
    var resultTable = resultTables.FirstOrDefault();
    }
}


In the above code snippet, note the SPSite URL which should be URL of the site whose search results you wish to query.

In the previous version of SharePoint, RowLimit actually had no limits. But in newer versions, it has the maximum limit of 500. This number is however configurable if we run PowerShell script on the SharePoint Search Service Application.

Also, note that now, we cannot directly execute the Query. Earlier we would simply call the method

KeywordQuery.ExecuteQuery()

and it would fetch the results for us. Now, SP 2013 introduces a new class SearchExecutor specifically built to execute queries. So in order to run a query, we would first have to initialize this class and then call its ExecuteQuery method on our KeywordQuery.

ResultType class and its corresponding KeywordQuery.ResultType property are also now obsolete. Instead we would now need to call a filter method on our resultant collection to get our desired results (see the code above).

Notice the query text in the above snippet. It is much similar to what was used in SP 2010.

Another important thing to notice above is that we would need to explicitly specify which columns should be returned in the query result. Of course, it was done earlier and in much simpler fashion using FullTextSQLQuery. Now, we would need to call the method –

keywordQuery.SelectProperties.Add("<column_name>")

to return this column in the result table collection.


Approach 2 – KeywordQuery using FQL
  
static void Main(string[] args)
{
    SPSite site = new SPSite("<url_whose_results_you_wish_to_query>");
    SearchServiceApplicationProxy proxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.GetContext(site));
    using (KeywordQuery keywordQuery = new KeywordQuery(proxy))
    {
        keywordQuery.ResultsProvider = SearchProvider.Default;
        keywordQuery.SourceId = new Guid("<RESULT_SOURCE_GUID>");
        keywordQuery.EnableFQL = true;
        keywordQuery.StartRow = 0;
        keywordQuery.RowLimit = 500;
        keywordQuery.EnableStemming = true;
        keywordQuery.TrimDuplicates = false;
        keywordQuery.AuthenticationType = QueryAuthenticationType.PluggableAuthenticatedQuery;
        keywordQuery.Culture = CultureInfo.CurrentCulture;
        keywordQuery.KeywordInclusion = KeywordInclusion.AllKeywords;
        keywordQuery.QueryText = "and(scope:string(\"All Sites\"), or(contentclass:string(\"STS_Site\") , contentclass:string(\"STS_Web\")))";
        keywordQuery.QueryText = "and(scope:string(\"All Sites\"), or(contentclass:STS_Site, contentclass:STS_Web))";
        keywordQuery.SelectProperties.Add("path");
        keywordQuery.SelectProperties.Add("title");
        keywordQuery.SelectProperties.Add("contentclass");
        SearchExecutor executor = new SearchExecutor();
        ResultTableCollection resultTableCollection = executor.ExecuteQuery(keywordQuery);
        var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
        var resultTable = resultTables.FirstOrDefault();
    }
}


Notice the highlighted lines of code in Cyan color in the above snippet. Only those lines are different from what we saw earlier. Rest all settings remain the same. in the above example, i have used two types of FQL queries. You can use either of them.

SourceID needs to be explicitly mentioned here because in SharePoint 2013, we need to create a custom Result Source which possesses a different query string in it. I would cover creating Result Sources in SharePoint 2013 in a separate post.


Happy coding !!

Wednesday, August 21, 2013

Initializing WCF client programmatically


Usually, developers follow the practice of making use of Web.config to set all the configuration values for a WCF client. But in some cases, that approach is not always applicable.

For example, if we are trying to call a WCF service through some SharePoint page which was deployed using a feature, in this case, the Web.config resides at a different location and hence the code would not be able to fetch this data.

The more desired approached is to initialize it through your code itself.

Below is the code which I had used to initialize a WCF Client for a service which uses Custom Binding and SOAP 1.2

using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

public static CustomBinding GetBinding()
{
   CustomBinding binding = new CustomBinding();
   binding.Name = "Name_of_your_binding";
   binding.CloseTimeout = TimeSpan.Parse("00:05:00");
   binding.OpenTimeout = TimeSpan.Parse("00:05:00");
   binding.ReceiveTimeout = TimeSpan.Parse("00:10:00");
   binding.SendTimeout = TimeSpan.Parse("00:10:00");
   binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap12, System.Text.Encoding.UTF8));
   HttpsTransportBindingElement hbe = new HttpsTransportBindingElement();
   hbe.RequireClientCertificate = true;
   hbe.AllowCookies = false;
   hbe.AuthenticationScheme = System.Net.AuthenticationSchemes.Anonymous;
   hbe.BypassProxyOnLocal = false;
   hbe.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
   hbe.KeepAliveEnabled = true;
   hbe.ManualAddressing = false;
   hbe.MaxBufferPoolSize = Convert.ToInt64(int.MaxValue);
   hbe.MaxBufferSize = int.MaxValue;
   hbe.MaxReceivedMessageSize = Convert.ToInt64(int.MaxValue);
   hbe.ProxyAuthenticationScheme = System.Net.AuthenticationSchemes.Anonymous;
   hbe.Realm = "";
   hbe.TransferMode = TransferMode.Buffered;
   hbe.UnsafeConnectionNtlmAuthentication = false;
   hbe.UseDefaultWebProxy = true;
   binding.Elements.Add(hbe);
   return binding;
}


public static EndpointAddress GetEndpointAddress()
{
   EndpointAddress endpoint = new EndpointAddress(<URL_string>);
   return endpoint;
}


public static void Main(string[] args)
{
   CustomBinding binding = GetBinding();
   EndpointAddress endpoint = GetEndpointAddress();
   <Proxy_Client_Name> client = new <Proxy_Client_Name>(binding, endpoint);
}

Above, Proxy_Client_Name denotes the name specified for your Client Class in the WCF Service Proxy file which you might have generated from your WSDL or must have been handed over to you by the WCF service providers.

One of the drawbacks of using this approach is that you cannot change the configuration settings at run-time. Apart from that, I would personally suggest all of you to go ahead with it :) ...  

Monday, August 19, 2013

Console Application to store User Profile Data into a CSV file


This post is gonna help you create a simple console application which would query the User Profile Application of your SharePoint farm and store its user profile data into a CSV file.

Let us first create two public classes - one corresponding to a CSV row and the other corresponding to the Writer object -

Class 1 - extended from a List of type string
 
 public class CsvRow : List<string>
{
    public string LineText { get; set; }
}

Class 2 - extended from StreamWriter class. It also includes logic to handle special characters in data

public class CsvFileWriter : StreamWriter
{
    public CsvFileWriter(Stream stream): base(stream)
    {
    }

    public CsvFileWriter(string filename): base(filename)
    {
    }

    public void WriteRow(CsvRow row)
    {
        StringBuilder builder = new StringBuilder();
        bool firstColumn = true;
        foreach (string value in row)
        {
// Add separator if this isn't the first value
          if (!firstColumn)
              builder.Append(',');
// Implement special handling for values that contain comma or quote
// Enclose in quotes and double up any double quotes
          if (value.IndexOfAny(new char[] { '"', ',' }) != -1)
              builder.AppendFormat("\"{0}\"", value.Replace("\"", "\"\""));
          else
              builder.Append(value);
          firstColumn = false;
        }
        row.LineText = builder.ToString();
        WriteLine(row.LineText);
    }
}


Now the next step's the very basic, creating the Service Context object in the main function as follows -

SPServiceContext _servicecontext = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, new SPSiteSubscriptionIdentifier(Guid.Empty));

UserProfileManager CurrentUserProfileManager = new UserProfileManager(_servicecontext);

UserProfileConfigManager CurrentUserProfileConfigManager = new UserProfileConfigManager(_servicecontext);


Now, we would create an object of the above custom CsvFileWriter class and include the rest of the code in it -

using (CsvFileWriter writer = new CsvFileWriter(@"E:\Results.csv"))
{
    var Profiles = CurrentUserProfileManager.GetEnumerator();
    CsvRow columnRow = new CsvRow();
    columnRow.Add("Property1");
    columnRow.Add("Property2");
    columnRow.Add("Property3");
    writer.WriteRow(columnRow);
    while (Profiles.MoveNext())
    {
         UserProfile profile = (UserProfile)Profiles.Current;
         CsvRow row = new CsvRow();
         row.Add(profile["<Property1_Name>"].Value.ToString());
         row.Add(profile["<Property2_Name>"].Value.ToString());
         row.Add(profile["<Property3_Name>"].Value.ToString());
         writer.WriteRow(row);
    }

}

Happy Coding ppl ...