Thursday, 7 September 2017

Backup-SPSite : You must specify a filename for the backup file.

I followed the backup instructions from here to backup my site collection but I encountered the following error: You must specify a filename for the backup file

That was strange as I have set the -Path parameter. The resolution is that the path MUST BE VALID. In my case , I was missing an underscore.

Sunday, 3 September 2017

SharePoint 2010: Unable to open Central Administration

I have been trying to install a SharePoint 2010 development environment on my local Windows 10 laptop (dont ask why) and I have finally been able to load Central Admin. Here are the issues I encountered:

1. Ensure you have Windows 10 Pro - you will need to activate Windows Auth before you install
2. Run the following script if Central Admin is

start /w pkgmgr /iu:IIS-WebServerRole;IIS-WebServer;IIS-CommonHttpFeatures;IIS-StaticContent;IIS-DefaultDocument;IIS-DirectoryBrowsing;IIS-HttpErrors;IIS-ApplicationDevelopment;IIS-ASPNET;IIS-NetFxExtensibility;IIS-ISAPIExtensions;IIS-ISAPIFilter;IIS-HealthAndDiagnostics;IIS-HttpLogging;IIS-LoggingLibraries;IIS-RequestMonitor;IIS-HttpTracing;IIS-CustomLogging;IIS-ManagementScriptingTools;IIS-Security;IIS-BasicAuthentication;IIS-WindowsAuthentication;IIS-DigestAuthentication;IIS-RequestFiltering;IIS-Performance;IIS-HttpCompressionStatic;IIS-HttpCompressionDynamic;IIS-WebServerManagementTools;IIS-ManagementConsole;IIS-IIS6ManagementCompatibility;IIS-Metabase;IIS-WMICompatibility;WAS-WindowsActivationService;WAS-ProcessModel;WAS-NetFxEnvironment;WAS-ConfigurationAPI;WCF-HTTP-Activation;WCF-NonHTTP-Activation

NOTE: This is not mine and has been borrowed from sensoft2000-sharepoint (thank you very much)

Sunday, 6 August 2017

SharePoint 2010 on Windows 10: Solving COMException / Unknown error (0x80005000)

This is not a problem I expected to solve, but it happened anyway. A client is using SharePoint 2010 and I need a local development farm.

I started with the usual configuration requirements:
Add <Setting Id="AllowWindowsClientInstall" Value="True"/> to the Setup config

Created a script to make a new configuration database so that I dont have to join a domain
$secpasswd = ConvertTo-SecureString "MyVerySecurePassword" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("mydomain\administrator", $secpasswd)

$guid = [guid]::NewGuid();
$database = "spconfig_$guid"

New-SPConfigurationDatabase -DatabaseName $database -DatabaseServer myservername\SharePoint -FarmCredentials $mycreds -Passphrase (ConvertTo-SecureString "MyVerySecurePassword" -AsPlainText -force)

The PowerShell script was generating the error.

The solution is simple - you need to enable IIS 6.0 Management Compatibility on the machine

The script ran and I now have a very old SharePoint farm to use

Wednesday, 2 August 2017

Azure: Why is my blob 0KB when I move it?

I was archiving documents in Blob Storage and found that the target files were always being rendered as 0 KB.

I was using the following code:

var storageAccount = CloudStorageAccount.Parse("CONNECTIONSTRING");
var blobContainer = storageAccount.CreateCloudBlobClient().GetContainerReference("container");
var blob = blobContainer.GetBlobReference("myfilename")

// Get the source as a stream
Stream stream = new MemoryStream();
blob.DownloadToStream(stream);

var destBlob = blobContainer.GetBlockBlobReference(blob.Name);
destBlob.UploadFromStream(stream);

blob.Delete();

The code should work, except for the fact that the stream needs to be repointed to the start for the download to work.

blob.DownloadToStream(stream); (from above)
stream.Position = 0;

And now the copy/move works

C#: How do I call an API from a console application?

I have used the following code to call an API and pass in some parameters in the request header.
Have a read here about the Encoding.GetEncoding(1252).

private static string ExecuteAPI(string url, Dictionary<string, string> parameters, string method = "POST", string body = " ", int timeOut = 180000)
{
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = method;

    foreach (var param in parameters)
    {
        request.Headers.Add(param.Key, param.Value);
    }

    if (method == "POST")
    {
        if (!string.IsNullOrEmpty(body))
        {
            var requestBody = Encoding.UTF8.GetBytes(body);
            request.ContentLength = requestBody.Length;
            request.ContentType = "application/json";
            using (var requestStream = request.GetRequestStream())
            {
                requestStream.Write(requestBody, 0, requestBody.Length);
            }
        }
        else
        {
            request.ContentLength = 0;
        }
    }
   else
   {
      request.ContentLength = 0;
   }

    request.Timeout = timeOut;
    request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);

    string output = string.Empty;
    try
    {
        using (var response = request.GetResponse())
        {
            using (var stream = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(1252)))
            {
                output = stream.ReadToEnd();
            }
        }
    }
    catch (WebException ex)
    {
        if (ex.Status == WebExceptionStatus.ProtocolError)
        {
            using (var stream = new StreamReader(ex.Response.GetResponseStream()))
            {
                output = stream.ReadToEnd();
            }
        }
        else if (ex.Status == WebExceptionStatus.Timeout)
        {
            output = "Request timeout is expired.";
        }
    }

    return output;
}

Load data into a SQL Database using SqlBulkCopy, Entity Framework and reflection

I recently had the requirement to bulk load data into a SQL database. Easy enough, but since I was using the Entity Framework, most of the work has already been done for me.

The plan is as follows:
Use SqlBulkCopy with a DataTable as an input. In order to do this, I will need to create some headers and then add the required data as rows before blasting them into the SQL Database.

ConcurrentQueue<MyObject> items = GetMyItems(); // method not included

Type t = (new MyObject()).GetType();
var dt = SetHeader(t);
AddItems(dt, items);
Load(dt, "MyTable");

// Add the properties as columns to the datatable
private DataTable SetHeader(Type t)
{
var dt = new DataTable();
PropertyInfo[] pi = t.GetProperties();

foreach (var p in pi)
{
if (string.Compare(p.PropertyType.Name, typeof(Guid).Name, true) == 0)
{
dt.Columns.Add(p.Name, typeof(Guid));
}
else
{
dt.Columns.Add(p.Name);
}
}

return dt;
}

// add each row as a CSV to the DataTable
private bool AddItems(DataTable dt, ConcurrentQueue<MyObject> items)
{
object table = new object();
Parallel.ForEach(items, item =>
{
object[] values = ObjectToArray(item);
lock (table)
{
dt.Rows.Add(values);
}
});

return true;
}

private bool Load(DataTable dt, string tableName)
{
var copy = new SqlBulkCopy(Constants.DATABASE_CNN);
copy.DestinationTableName = tableName;
copy.WriteToServer(dt);
return true;
}

// convert the object to a string array
private string[] ObjectToArray(object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj", "Value can not be null or Nothing!");
}

Type t = obj.GetType();
PropertyInfo[] pi = t.GetProperties();

var returnValue = new string[pi.Length] ;

for (int index = 0; index < pi.Length; index++)
{
if (pi[index].GetValue(obj) == null)
{
returnValue[index] = "";
}
else
{
returnValue[index] = pi[index].GetValue(obj).ToString();
}
}

return returnValue;
}

Friday, 21 July 2017

C#: How to I extend the timeout of a WebClient call? (30 seconds is just not long enough)

My Azure function is calling web services that take longer than 30 seconds to complete, so I needed to extend the default timeout the WebClient class.

I created the following base class:

using System;
using System.Net;

namespace MyHelpers
{
    public class MyWebClient : WebClient
    {
        public int Timeout { get; set; }

        public WebDownload() : this(180000) { }

        public WebDownload(int timeout)
        {
            this.Timeout = timeout;
        }

        protected override WebRequest GetWebRequest(Uri address)
        {
            var request = base.GetWebRequest(address);
            if (request != null)
            {
                request.Timeout = this.Timeout;
            }
            return request;
        }
    }
}

I set the default to 3 minutes. I can then use it like I would a usual WebClient call:

private string ExecuteAPI(string url, Dictionary<string, string> parameters)
{
    try
    {
        using (var client = new MyWebClient())
        {
            foreach (var param in parameters)
            {
                client.Headers.Add(param.Key, param.Value);
            }

            return client.DownloadString(url);
        }
    }
    catch
    {
        return string.Empty;
    }
}

Thursday, 20 July 2017

C#: How do I split a generic list into smaller chunks?

When trying to send some records to an Azure Service Bus payload, I found that due to the limits of the tenant subscription, the Service Bus could not handle the large payload I was sending. I have 256K to deal with and 1000+ records to load.

A simple solution is to group the items in a logical manner, and then split them up into equal sized batches. LINQ to the rescue.

Grouping the items is done as follows:
var items = new List<MyObject>();
// add all the items the list
var records = items.GroupBy(a => a.MyGroupingProperty);

Then batch it up:
var batchSize = 10;
var batchedItems = records
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / batchSize)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();

batchedItems is now grouped into chunks of 10. I can now iterate though the batches and handle them accordingly:

foreach(var batchedItem in batchedItems)
{
// magic goes here.
}


Wednesday, 19 July 2017

Entity Framework: No Entity Framework provider found for the ADO.NET provider with invariant name 'System.Data.SqlClient'

The solution is simple - install the Entity Framework Nuget package to the solution startup project.

Entity Framework: No connection string named 'ImportEntities' could be found in the application config file.

I got this message when writing a PoC application for Azure. The message is quite self explanatory, but the key is WHICH PROJECT TO UPDATE. I have multiple projects, but my startup was a Console Application (for testing).

The solution is to add the setting (in app.config or web.config) to the solution STARTUP PROJECT.

<configuration>
  <connectionStrings>
    <add name="ImportEntities" connectionString="...." />
  </connectionStrings>
</configuration>

Azure Blob Storage: How can I quickly read a text file?

My current project has the requirement to read a large text file from blob storage and then process the contents. Getting the file is easy:

var storageAccount = CloudStorageAccount.Parse(CONNECTIONSTRING);
var container = storageAccount.CreateCloudBlobClient().GetContainerReference(containerName);
var blob =  container.GetBlobReference(fileName);

And the data can be easily read:

Stream stream = new MemoryStream();
blob.DownloadToStream(stream);
stream.Position = 0;

string text = "";
using (StreamReader reader = new StreamReader(stream))
{
text = reader.ReadToEnd();
}
var lines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

The real time saver came when I had to process the contents. A traditional IEnumerable was too slow, but luckily Parallel saved the day:

Parallel.For(0, lines.Length - 1, i =>
{
DoSomethingWithTheRow(lines[i]);
});

Its lightening fast.

A word of warning: I passed a generic into the function (List<MyCustomType>) and found out very quickly that it is not thread safe. I changed to ConcurrentQueue<MyCustomType> and the issue was resolved.

Monday, 17 July 2017

Azure API: A route named 'swagger_docs' is already in the route collection. Route names must be unique

While creating some PoC APIs, I encountered this little gem.

Fortunately, a simple fix is available: In the 'Settings' tab of the 'Publish' dialog, click 'Remove additional files at destination' (in the File Publish Options expander).


You can then republish the API and the issue should be resolved.

Tuesday, 11 July 2017

C#: How can I create a class dynamically from text?

The following code is a really simple way to create a class dynamically from string.

 dynamic data = new ExpandoObject();

IDictionary<string, object> dictionary = (IDictionary<string, object>)data;
dictionary.Add("FirstName", "Richard");
dictionary.Add("Surname", "Leeman");

Console.WriteLine(data.FirstName + " " + data.Surame);

Wednesday, 22 March 2017

Azure Service Bus: Serialization operation failed due to unsupported type

When trying to add an object property to my Service Bus Queue, I encountered the following error:

"Serialization operation failed due to unsupported type RichardLeeman.Model.MyObject."

So I added the  [Serializable] annotation to the class, but to no avail. The issue seems to be that the property bag only likes simple types like int or string.

So, my simple fix was to convert the class to JSON and put that in the bus. I used System.Web.Script.Serialization.JavaScriptSerializer to convert the object and placed the resulting string in the property bag.

var client = QueueClient.CreateFromConnectionString(SERVICE_BUS_CNN, "myqueue");
var json = new JavaScriptSerializer().Serialize(myObjectInstance);
var message = new BrokeredMessage("my test message");
message.Properties.Add(new KeyValuePair<string, object>("MyObjectProperty",json));
client.Send(message);





Tuesday, 21 March 2017

Swagger: How do I add custom return codes to my Web API?

Swagger is great tool to document APIs, The standard return codes (200 - OK) do not always give enough detail about the endpoint. Fortunately, there are some simple annotations that can be added to give a better description about the return code.

using Swashbuckle.Swagger.Annotations;
using System.Web.Http;

namespace MyNameSpace
{
    public class MyAPIController : ApiController
    {
        [HttpGet]
        [Route("api/my_custom_api_route")]
        [SwaggerResponse(System.Net.HttpStatusCode.OK, "All good here, thanks for asking")]
        [SwaggerResponse(601, "foo message")]
        public bool MyAPI()
        {
            return true;
        }
    }
}

The result is as follows:



Monday, 20 March 2017

Azure: How do I delete my function?

I have been playing with Azure functions (which are great), but there does not seem to be an intuitive way to delete them.

The first option (which requires forethought) is to create its own Resource Group (with the associated storage accounts) and delete the RG. Easy - if you knew it was coming and planned for it.

The other option is go into 'Function App Settings'  -> 'Go to App Service Settings'.




You can then delete the app.


Thursday, 9 March 2017

Azure Function: Call a web service and return the result as a class

My current project is using Azure Functions as action triggers. Thanks to some nifty work from the Visual Studio team, we now have a local development environment.

The following POC function will call a Web API (configured through API Management) and convert the resultant JSON into a class object.

1. Create a 'Model' solution that stores the class to be used. I called my MyModel. My class is called ApiResult.

namespace MyModel
{
    public class ApiResult
    {
        public string Message { get; set; }
    }
}

2. Create an API to invoke

using MyModel;
using System.Web.Http;

namespace MyControllers
{
    public class MyController : ApiController
    {
        [System.Web.Http.HttpGet]
        [System.Web.Http.Route("api/myapiendpoint")]
        public ApiResult ValidateFile()
        {
            return new ApiResult()
            {
                Message = "Success!"
            };
        }
    }
}

3. Copy the MyModel.dll to a folder in my Azure Function folder (I called in References)

4. Here is the code to call the endpoint and return the result object

// add the dll references
#r "..\References\MyModel.dll"
#r "..\References\System.Runtime.Serialization.dll"

// add the namespaces
using System.Net;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
using MyModel;

// I used an HttpTrigger action for simplicity
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    // create a few constants to make things easier
    const string WEBSERVICE_URL = @"http://myapimanagement.azure-api.net/api/myapiendpoint";
    const string SUBSCRIPTION_KEY = @"123456";
 
    try
    {
        // Call a method to do the work
        ExecuteWebRequest(WEBSERVICE_URL, SUBSCRIPTION_KEY);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    return req.CreateResponse(HttpStatusCode.OK);
}

private static void ExecuteWebRequest(string url, string key)
{
    WebRequest webRequest = WebRequest.Create(url);
    if (webRequest != null)
    {
        webRequest.Method = "GET";
        webRequest.Timeout = 20000;
        webRequest.ContentType = "application/json";
        webRequest.ContentLength = 0;

        // Add the subscription key to the header so the call will authenticate
        webRequest.Headers.Add("Ocp-Apim-Subscription-Key", key);
        using (System.IO.Stream s = webRequest.GetResponse().GetResponseStream())
        {
            using (System.IO.StreamReader sr = new System.IO.StreamReader(s))
            {
                // convert the Json to the base object
                var jsonResponse = ReadApiResultToObject(sr.ReadToEnd());
            }
        }
    }
}

private static ApiResult ReadApiResultToObject(string json)
{
    ApiResult deserializedUser = new ApiResult();
    using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
    {
        DataContractJsonSerializer ser = new DataContractJsonSerializer(deserializedUser.GetType());
        deserializedUser = ser.ReadObject(ms) as ApiResult;
    }
    return deserializedUser;
}

My biggest learning from this exercise is that the compilation is late bound, so any errors will be at runtime. So, make small changes and test often.

Azure SQL Database: Cannot connect to XXXXX.database.windows.net PART 2

My first attempt to resolve this issue (see this post), I was able to get past the first barrier.

However, for some reason, my SSO credentials were not working. My account was configured as the system administrator account, but I could not authenticate.

Then I remember that my user password was different AT THE TIME THE SQL SERVER INSTANCE WAS CREATED. I was successfully able to connect using SQL Authentication with my username and the OLD PASSWORD.

Azure SQL Database: Cannot connect to XXXXX.database.windows.net

While creating an Azure POC, I create a new SQL Database and tried to connect via the online tools. I then received the following message:



Fortunately, the resolution is very simple. Open the database and navigate to the Firewall Settings at the top of the Overview blade.

Select 'Add client IP' to resolve the problem.


Sunday, 26 February 2017

"C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\DotNet\Microsoft.DotNet.Props" was not found

I recently opened a sample project from a colleague and encountered the following error:

"C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\DotNet\Microsoft.DotNet.Props" was not found

Fortunately, the solution was simple enough:
1. Update the Visual Studio CE 2015 Update 3
2. Install .Net Core 1.0.1 tools Preview 2

All the links are available here:
https://www.microsoft.com/net/core#windowsvs2015

Sunday, 15 January 2017

C#: How do I send an email from my owa site?

I recently had the requirement to send out multiple emails from my OWA account. Being a lazy person, I deceided that it would be better to automate the process instead than send it one at a time.

Here is the code I generated to quickly send a test email from the account. The OWA site is https://mail.myowa.com.au/owa.

using Microsoft.Exchange.WebServices.Data;
using System;

namespace Email
{
    class Program
    {
        static void Main(string[] args)
        {
            ExchangeService service = new ExchangeService();
            service.Credentials = new WebCredentials("username", "password", "domain");
            service.Url = new Uri(@"https://mail.myowa.com.au/EWS/Exchange.asmx");

            EmailMessage message = new EmailMessage(service);

            // Add properties to the email message.
            message.Subject = "Test Subject";
            message.Body = new MessageBody()
            {
                BodyType = BodyType.HTML,
                Text = "<b>Test content in bold</b>"
            };
            message.ToRecipients.Add("mytargetaddress@domain.com");

            // Send the email message and save a copy.
            message.SendAndSaveCopy();
        }
    }
}​