Tuesday, March 26, 2013

Uploading large files to SharePoint Online (O365)

It seems amazing to me that Microsoft did such a poor job of giving developers a good way to upload large documents to Microsoft SharePoint Online (aka Office 365 or SharePoint 365). Ideally I would like to use the

Microsoft.SharePoint.Client.File.SaveBinaryDirect

method that is part of the CSOM (Client Side Object Model), but this does not work with SharePoint Online and only seems to work on SharePoint 2010 (probably 2013, but have not tested). I did get

Microsoft.SharePoint.Client.File uploadedFile = docLib.RootFolder.Files.Add(newFileFromComputer);

to work on SharePoint Online, but it not very useful because it is limited to files of about 3MB in size.

After lots of trial and error, I figured out that the most reliable was to do it is using a standard PUT request and passing the Claims Authentication cookies that are required to make most any request to SharePoint Online. This works well unless you are debugging and you still have the default exception warning enabled. In that case, for large files that take more than 60 seconds to upload you will get a message similar to this:

The CLR has been unable to transition from COM context 0x1fe458 to COM context 0x1fe5c8 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

Depending on what you are doing this may be okay. In my case, I don’t care if my command line application waits for a long request to continue. This is caused by using the STA threading attribute on the main method of the application.

Below are two methods (one just an overload that calls the other basically) that upload a document to a shared library in SharePoint Online. It should also work on SharePoint 2010 plus if you replace the ClaimClientContext with a standard ClientContext that is needed for SharePoint 2010+. I’ll leave that to you to try on your own for now. You’ll notice I have also added functionality to change the created and modified date of the files after they are uploaded. For details (and source code click here). Or if you just want to understand more about Claims based authentication, sample code, options, etc definitely check out here.Lastly, the keyValues dictionary is just a dictionary of actual internal field names (see CAML field names) as keys and the values for each field are the values of the dictionary.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Client;
using MSDN.Samples.ClaimsAuth;
using System.Xml.Linq;
using System.Net;
using System.IO;
...

public static void AddDocument(string fileToUpload, string webUrl, string docLibInternalName, string docLibUIName, string documentSetName, string filenameToSaveAs, DateTime? createdDate, DateTime? modifiedDate, Dictionary<string, object> keyValues, int timeoutInMilliseconds)
{
string urlToSaveAs = webUrl + "/" + docLibInternalName + "/" + documentSetName + "/" + filenameToSaveAs;
AddDocument(fileToUpload, webUrl, urlToSaveAs, createdDate, modifiedDate, keyValues, timeoutInMilliseconds);
}

// Uploads most any size file to SharePoint Online (O365) using Claims Authentication. It does NOT use CSOM, and instead uses a standard PUT request
// that has the cookies from the Claims based authentication added to it.
// This solution is based on
http://stackoverflow.com/questions/15077305/uploading-large-files-to-sharepoint-365
// and
// To get the claims authentiation cookie, this solution requires:
http://msdn.microsoft.com/en-us/library/hh147177.aspx#SPO_RA_Introduction
// or if you want to get the cookies for claims authentication antoher way, you can use
//
http://www.wictorwilen.se/Post/How-to-do-active-authentication-to-Office-365-and-SharePoint-Online.aspx
public static void AddDocument(string fileToUpload, string webUrl, string urlToSaveAs, DateTime? createdDate, DateTime? modifiedDate, Dictionary<string, object> keyValues, int timeoutInMilliseconds)
{

//For example: byte[] data = System.IO.File.ReadAllBytes(@"C:\Users\me\Desktop\test.txt");
byte[] data = System.IO.File.ReadAllBytes(fileToUpload);

// get the cookies from the Claims based authentication and add it to the cookie container that we will then pass to the request
CookieCollection cookies = ClaimClientContext.GetAuthenticatedCookies(webUrl, 200, 200);
CookieContainer cookieContainer = new CookieContainer();
cookieContainer.Add(cookies);

// make a standard PUT request
System.Net.ServicePointManager.Expect100Continue = false;
HttpWebRequest request = HttpWebRequest.Create(urlToSaveAs) as HttpWebRequest;
request.Method = "PUT";
request.Accept = "*/*";
request.ContentType = "multipart/form-data; charset=utf-8";
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = false;
request.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)";
request.Headers.Add("Accept-Language", "en-us");
request.Headers.Add("Translate", "F");
request.Headers.Add("Cache-Control", "no-cache");
request.ContentLength = data.Length;
request.ReadWriteTimeout = timeoutInMilliseconds;
request.Timeout = timeoutInMilliseconds;


using (Stream req = request.GetRequestStream())
{
    req.ReadTimeout = timeoutInMilliseconds;
    req.WriteTimeout = timeoutInMilliseconds;
    req.Write(data, 0, data.Length);
}

// get the response back
HttpWebResponse response = null;
try
{
    response = (HttpWebResponse)request.GetResponse();
    Stream res = response.GetResponseStream();
    using (StreamReader rdr = new StreamReader(res))
    {
        string rawResponse = rdr.ReadToEnd();
       
    }
}
catch (Exception ex)
{
    throw ex;
}
finally
{
    if (response != null)
    {
        response.Close();
    }
}

// NOTE: the file that was uploaded is still checked out and must be checked in before it will be available to others.
// The method includes a checkin command. If the method below is removed for some reason,
// a checkin method call should be added here so that the file will be available to all (that have access).
// NOTE: The method calls add keyValues passed in as well. These would need to be done also if the method is removed.
UriBuilder urlBuilder = new UriBuilder(urlToSaveAs);
string serverRelativeUrlToSaveAs = urlBuilder.Path;
ChangeCreatedModifiedInfo(webUrl, serverRelativeUrlToSaveAs, createdDate, modifiedDate, keyValues);

}

 

Tuesday, March 19, 2013

Keep non-Microsoft software up-to-date

If you have ever wanted to keep non-Microsoft software on you computer up-to-date from one place, you can use the following software for free (personal use only).

http://secunia.com/vulnerability_scanning/personal/

Check it out!

Monday, February 25, 2013

Get all sites in a SharePoint web application using PowerShell

I realize the title of this entry is ambiguous. Specifically, you might be wondering if I mean site or site collection. The answer is: Yes. To make it less ambiguous, I will be referring to how things are in code, not the UI. The difference is that in code SPSite is actually a Site Collection (in the UI) and in code SPWeb is actually a Site (in the UI). The hierarchy of objects goes: SPWebApplication | SPSite | SPWeb where SPWebApplication and SPSite have Collection of the other objects below them. Below are example of how to get both SPSites and SPWebs.

Get number of SPWebs in your web application

$allwebs = Get-SPWebApplication http://www.sharepoint.com | Get-SPSite -Limit All | Get-SPWeb -Limit All
$allwebs.Count

Get urls of all the SPWebs in your web application

Get-SPWebApplication http://www.sharepoint.com | Get-SPSite -Limit All | Get-SPWeb -Limit All | select Url

Get number of SPSites in your web application

$allwebs = Get-SPWebApplication http://www.sharepoint.com | Get-SPSite -Limit All | Get-SPWeb -Limit All
$allwebs.Count

Get urls of all the SPSites in your web application

Get-SPWebApplication http://www.sharepoint.com | Get-SPSite -Limit All | Get-SPWeb -Limit All | select Url

Tuesday, February 19, 2013

MySql error message: Parameter '@A' must be defined using .NET connector

I upgraded my MySql .NET connector (MySql.Data.dll) from 1.0.7.30072 to a much newer 6.5.4.0 version. I was expecting complete backward compatibility, but I was completely wrong in my assumption. Instead I started getting the following error.

Fatal error encountered during command execution.  

  at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
   at MySql.Data.MySqlClient.MySqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet)

The problem is that “Fatal error encountered during command execution” was not particularly useful. As it turned out we had accidentally deployed the new MySql.Data.dll to QA and production without noticing that some queries failed with the above message. Then I tried to reproduce the problem on my laptop and could not. My project in Visual Studio 2010 had a reference to v1 of the MySql.Data.dll. Once I figured out there was a different version that snuck into our development environment and made it to production I figured that was the issue. So, I changed the reference in VS2010, but it seemed to be using the older version still. I’m not sure exactly how and I didn’t take the time to figure out why, and only made everything more confusing. In the end, I created a command line app, and added the new version of the MySql.Data.dll to the project and added just the snippet of code that was breaking. Thank goodness I could now reproduce the error on my laptop. Now I looked at the inner exception, and I see what the real error message is:

Parameter '@A' must be defined.

Okay, now we are getting somewhere. After a bit of searching I figured out that I had to add “Allow User Variables=True” to my connection string (not the SQL, but the connection string in the config file).

The solution: Just add

Allow User Variables=True

to your config file and you can now use user defined variables in your sql statements that you pass to the MySql .NET connector.

Tuesday, February 12, 2013

Finding Currently Running Query using T-SQL

To find out what queries are currently running on your SQL Server try the following query.

SELECT sqltext.TEXT,
req.session_id,
req.status,
req.command,
req.cpu_time,
req.total_elapsed_time
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext

Keep in mind that the results of this query will include this query itself, so the result will always be at least one row returned.

If you decide you want to kill one of the queries you can use the kill sql statement and the session id (see the second column).

The system is simple:

KILL <session id here>

I owe the basis for this post to the following post: http://blog.sqlauthority.com/2009/01/07/sql-server-find-currently-running-query-t-sql/

Friday, February 8, 2013

Get a list of all user defined stored procedures, functions, etc

If you are using SQL Server 2005 and newer you can use the queries below to get a list of any user defined object (including, but not limited to stored procedures, scalar-valued functions, table-valued functions, aggregate functions, and views) for a given database on a SQL Server 2005 installation. For a complete list of objects that you can list click here. The queries below will give you the same results you get when you look in Object Explorer in SSMS (Microsoft SQL Server Managements Studio).

All User Defined Objects

This will give you all the objects listed here.

-- all user defined objects
SELECT  sys.schemas.name + '.' + sys.objects.name, type, type_desc
FROM    sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
order by 3,1

Common User Defined Objects

This will give you a list of the following user defined objects: stored procedures, views, table-values functions, scalar-valued functions, aggregate functions.

-- common user defined objects
SELECT  sys.schemas.name + '.' + sys.objects.name, type, type_desc
FROM    sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where type in
(
'p', 'pc', -- stored procs
'v', -- views
'tf', 'if', 'ft', -- table-valued functions
'fn', 'fs', -- scalar-valued functions
'af' -- aggregate functions
)
order by 3,1

User Defined Stored Procedures

-- user defined stored procs
SELECT  sys.schemas.name + '.' + sys.objects.name
FROM    sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where type in ('p', 'pc')
order by 1

User Defined Views

-- user defined views
SELECT  sys.schemas.name + '.' + sys.objects.name
FROM    sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where type='v'
order by 1

User Defined Table-Valued Functions

-- user defined table-valued functions
SELECT  sys.schemas.name + '.' + sys.objects.name
FROM    sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where type in ('tf', 'if', 'ft')
order by 1

User Defined Scalar-Valued Functions

-- user defined scalar-valued functions
SELECT  sys.schemas.name + '.' + sys.objects.name
FROM    sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where type in ('fn', 'fs')
order by 1

User Defined Aggregate-Valued Functions

-- user defined aggregate-valued functions
SELECT  sys.schemas.name + '.' + sys.objects.name
FROM    sys.objects
INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where type = 'af'
order by 1

Wednesday, January 23, 2013

Stored procedure for page results and sort by different columns

 

Here is a stored procedure for MS SQL Server that can be used to page the results and allow it to be sorted by different columns. It is fast and easy to use. Enjoy.

--GetPersonPage2005 5, 20, 'NAME DESC'
--GetPersonPage2005 null, 20, 'NAME DESC'
--GetPersonPage2005 5, 20, 'SEARCH_CODE ASC'

alter proc GetPersonPage2005
@StartRowIndex decimal(18,0),
@MaximumRows int,
@OrderBy varchar(50)
as

SELECT [NAME], SEARCH_CODE, CREATED
FROM
     (SELECT [NAME], SEARCH_CODE, CREATED,
               ROW_NUMBER() OVER
            (
            ORDER BY -- add columns to sort by here
                CASE @OrderBy WHEN 'NAME DESC' THEN  [NAME] END DESC,
                CASE @OrderBy WHEN 'NAME ASC' THEN  [NAME] END ASC,
                CASE @OrderBy WHEN 'SEARCH_CODE DESC' THEN  SEARCH_CODE END DESC,
                CASE @OrderBy WHEN 'SEARCH_CODE ASC' THEN  SEARCH_CODE END ASC,
                CASE @OrderBy WHEN 'CREATED DESC' THEN  CREATED END DESC,
                CASE @OrderBy WHEN 'CREATED ASC' THEN  CREATED END ASC
            ) as RowNum
      FROM ALL_PERSON e
     ) as Tbl
WHERE RowNum BETWEEN @StartRowIndex AND (@MaximumRows + @StartRowIndex - 1)   
Order by RowNum ASC
go