Wednesday, June 25, 2014

Activation error occurred while trying to get instance of type LogWriter, key ""

I have blogged about this error before and this post has become one of my most viewed. I hope it provides the answer you need and it makes sense. Please feel free to leave a comment!


Microsoft.Practices.ServiceLocation.ActivationException : Activation error occurred while trying to get instance of type LogWriter, key ""
 ----> Microsoft.Practices.Unity.ResolutionFailedException : Resolution of the dependency failed, type = "Microsoft.Practices.EnterpriseLibrary.Logging.LogWriter", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type LogWriter cannot be constructed. You must configure the container to supply this value.



It occurs when using Microsoft.Practices.EnterpriseLibrary for logging errors. There are lots of blogs that reference this error and they mostly say the error is caused by:

a) an error in your configuration (often the database connection string)
b) not all the DLLs are present

That was not my situation. I was logging just to the event log and I was certain I had all the DLLs in the bin directory of my web service. The error occurred when I deployed the web service to a new environment. 

I spent many hours tracking down the cause. In this case it was because Microsoft.Practices.EnterpriseLibrary was installed on the target environment and the DLLs were registered in the GAC. 

Now as you know, .NET will look for the location of DLLs in a specific order and the GAC is at the top of the list.  The problem is, when the Enterprise Library DLLs are installed in the GAC then it is unable to determine the location of the configuration file.  I guess that when they are just present in the bin directory the location of the configuration file is presumed to be "up a level".  Removing the DLLs from the GAC was not an option, I needed to find a way that would work with the DLLs in the bin directory or in the GAC.

I have a DLL that I use to simplify the calls when logging an error.  It has several constructors to create log entries of different severities.  Below is the code that includes just the critical error constructor. 

using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace Ciber.Common.Logging
{

 public static class Logging
    {

       public static void LogCritical(string message, string title, string category)
        {
            Logger.Write(message, category, 3, 1, TraceEventType.Critical, title);
        }

     }
}

Now I have many places in my code where I reference the LogCritical method and I didn't want to change them. What I needed was a way to link up to the loggingConfiguration section in my web.config.

Firstly I added two additional references and using statements

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;


Then I added this method to my class:

public static LogWriter CreateLogger()
{
   EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(ReadConfigSource());
   var logWriter = EnterpriseLibraryContainer.Current.GetInstance();
   return logWriter;
}


What that allows is to create a default container from an XML location that I specify and which in my case contains the loggingConfiguration section. 

I can then get an instance of the LogWriter class by calling EnterpriseLibraryContainer.Current.GetInstance(). 

Before I list the ReadConfigSource method let me first show the modified constructor from above which now reads:

public static void LogCritical(string message, string title, string category)
{
   LogWriter logger = CreateLogger();
   logger.Write(message, category, 3, 1, TraceEventType.Critical, title);
}

Note the lower case L on the logger object.  So great, my calls that write to the event log don't need to be changed and I have the ability to specify where my loggingConfiguration section is located. 

Below is the code for the ReadConfigSource() method.  Two very important points here.

1) I use OpenWebConfiguration() with HttpRuntime.AppDomainAppVirtualPath which will resolve correctly both in debug mode and in deployed mode.  Don't be tempted to use OpenWebConfiguration(null) or OpenWebConfiguration("/") which will work fine in Debug mode but will fail when deployed.

2) I added this to the start of my loggingConfiguration string @"<?xml version=""1.0"" encoding=""utf-8"" ?>".  Without it the loggingSection.ReadXml(xmlReader); line will fail. 

 public static IConfigurationSource ReadConfigSource()
{
   var configSource = new DictionaryConfigurationSource();
   string virpath = HttpRuntime.AppDomainAppVirtualPath;
   Configuration configFile = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(virpath);

   ConfigurationSection section = configFile.GetSection("loggingConfiguration");
   if (section != null)
   {
      string loggingConfig = @"<?xml version=""1.0"" encoding=""utf-8"" ?>";
      loggingConfig = loggingConfig + section.SectionInformation.GetRawXml();

      LoggingSettings loggingSection = null;
      using (var stringReader = new StringReader(loggingConfig))
      {
          var xmlReader = XmlReader.Create(stringReader,
             new XmlReaderSettings() { IgnoreWhitespace = true });

          loggingSection = new LoggingSettings();
          loggingSection.ReadXml(xmlReader);
          xmlReader.Close();
       }

       configSource.Add(LoggingSettings.SectionName, loggingSection);
     }

      return configSource;
}

I am pleased with the result.  Hope it works for you.

Wednesday, May 28, 2014

BizTalk Rules Engine - Lessons Learned

If you are looking for a few hours to while away then the BizTalk Rules Engine will certainly occupy the time.  Hopefully this post might give you some hours back. This post is about using the BizTalk Rules Engine within an orchestration.  In my case I want to set Boolean values that can then be used in decision shapes within the orchestration.  The sort of thing would be "if IsCreateCase is true then do this, else do that".  A quick summary of the things I learnt is:
  1. Write the results of the BRE rule back into the message
  2. Keep your rules really simple
  3. Find a workaround for null nodes
  4. Put the Call Rules shape in a scope and add an exception handler
The easiest thing is to write the BRE rule results back into the message.  When you set up the Call Rules shape it asks for only two things, the name of the BRE policy and the input parameters.
In my case the input is an XML message and if you create an Action to write back to a node in the message, it will actually clone the message. 

For me it was a benefit to have these results within the message as it provided a means of checking the logic was correct. I created some xs:boolean elements in my message and set the default value to false.  That is key because the target element must exist within the message.  Now all I had to think about was writing the conditions that will evaluate to true.

During testing of the policy in the Business Rules Composer I soon realised that it is best to keep the rules really simple if you can.  That is because when testing it displays the name of the rule that was fired so it is easier to check if these are simple rules. 

My next problem was around the message instances.  I am comparing the value of two nodes (both called CreateCase) in the message but in some valid message instances one of the nodes can be completely absent.

In code you would of course check for the existence of the Before/CreateCase node before checking its value because otherwise you would get en exception with the first example message.  I thought that BRE would do this if I used a condition which checked for its existence, but I just could not get that to work.  No combination of logical AND and OR would do the trick. 

In frustration I gave up and used the map which transforms the external schema to my internal one to add the necessary elements as nulls. I blogged about creating a null node here. I used the logical existence operator ? with the logical not ! along with the Value Mapping functoid.  Having a null element for CreateCase made all the difference and simplified my rules even more. 

Having tested my rules thoroughly in the Business Rules Composer I published and deployed it to BizTalk.  In the orchestration I added the Call Rules shape within a scope and created an Exception Handler that trapped errors of type PolicyExecutionException (you need to add a reference to Microsoft.Rule.Engine.DLL).

In the end I am very pleased with the result.  One simple shape determines the logic that my message will follow through the orchestration.  If I have to amend the rules I can do so without redeploying the orchestration. Sweet. 

Monday, April 28, 2014

BizTalk Deployment Framework 5.5 Errors

I was working with the BizTalk Deployment Framework version 5.5 and I kept hitting an error when deploying. The error arose with the xml pre-processor step as it tries to create the port bindings file form the master and the environment settings file.  The error was

InitSettingsFilePath:
Invalid settings file path (OK on server undeploy)

I traced it to the SettingsFilePath parameter being blank which is created during the MSBUILD process. 

It took me a while to realise it was because the Install Wizard with 5.5 does not prompt for the location of the Settings File as it did in previous versions. It now prompts for the account name used for configuring FILE Send and Receive Ports because it will now automatically create the file locations for you and set up permissions (hooray).

To solve the "Invalid settings file path" problem I had to edit the InstallWizard.XML file and add a new SetEnvUIConfigItem section to prompt for the settings file.  You can find the XML in the BTDF documentation.  After that, my deployment worked without error.

Another point is the BTDFPROF sample file has changed and it specifies several PropertyGroup sections where the name of the BizTalkHosts are specified.  I found this didn't work so I went back to adding the BizTalkHosts element in the ItemGroup section and included the host instance names  I wanted to bounce. 


Monday, March 10, 2014

Unit Tests and Microsoft Practices Logging

I created a WCF Web Service where I am logging errors using the Microsoft.Practices.EnterpriseLibrary tools for handling exceptions and logging.
When I called the web service using the WCF Test Client, all was well and I recorded the errors successfully.

But when I added some unit tests that would also generate an exception when calling the web service, I received the following error:

Microsoft.Practices.ServiceLocation.ActivationException : Activation error occured while trying to get instance of type LogWriter, key ""
 ----> Microsoft.Practices.Unity.ResolutionFailedException : Resolution of the dependency failed, type = "Microsoft.Practices.EnterpriseLibrary.Logging.LogWriter", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type LogWriter cannot be constructed. You must configure the container to supply this value.


I spent some time Googling and finally found this post which explains all.
You need to copy the relevant sections of the web.config that refer to Enterprise Library into the app.config of the Unit Test Project. 

This also applies if you have an AppSettings entry (e.g. a Dynamics CRM Connection string) as this also needs to be copied into the app.config.






 

Tuesday, February 4, 2014

SharePoint 2013 - Word has encountered a problem trying to open the file

I experienced this error "Word has encountered a problem trying to open the file" when trying to open a Word template (dotx) from SharePoint 2013.  I should explain that I was using Word 2010 and could open the template from its location in the file system, but when I uploaded it as a content type into SharePoint 2013 I received this error when trying to use the template.

Some blogs suggested the following
a) install Office 2010 32bit instead of Office 2010 64bit
b) upgrade to SP2
c) remove the SharePoint Foundation Services as a component of Office, and then do a repair.

I tried all of that and yet it still would not open the template.  Then I found a blog that advised switching off Protected mode - go to File, Options, and Trust Centre Settings and disable all the Protected options which are enabled by default.  That did the trick.

My advice - try switching Protected mode off first before trying anything else.  Now I come to think of it I had seen this before and had forgotten about it.  Hence the blog.

Tuesday, January 28, 2014

Change Date Format in Dynamics CRM 2013

Surprisingly I could not find a blog that told me how to change the date format in Dynamics CRM 2013. 

There are plenty of blogs which say how to di it for CRM 2011 but I already knew that.  So what has happened to the Personal Options that was accessible from the File menu?

You will find it in the top right hand corner as the little settings wheel.  that will give you access to the Options and the About box.  Once you have the Personal Options dialog open, then you change the date as you did before.  Go to the Formats tab, and change the current format to whatever you want. 

Sunday, January 26, 2014

Load a Word document into Internet Explorer and set Response.ContentType

About two years ago I wrote some code to construct a PDF document on the fly and display it in the browser. Yesterday I was trying to do the same thing for a Word document. If you've seen earlier posts this month you will see I am using Aspose Words for .Net to perform a mail merge.  I wanted the ability to preview the result before saving the output. 
 
Now when I did this previously for a PDF file I had a web service that returned a memory stream and I was able to load that into the Response.OutputStream object without difficulty. But I immediately ran into a problem with my web service which is built with .Net FW 4.0.  The memory stream I returned became a marshalled object which does not have the same properties for WriteTo() or ToArray() which meant I could not easily load it into Response.OutputStream.
 
I suppose I could have found a solution but instead I thought I would return it as a string instead. Alas that gave a new set of problems which I suspect was down to encoding when converting between the stream and the string.  This morning I tried converting to a base base64 string and that did the trick. 
 
So firstly here is the code in the web service that converts the memory stream to base64.


public string PreviewMerge(DataMergeRequest req)
{   // code to do mailmerge goes here
   MemoryStream msRawData = merge.MergeDataSet(ds, templatelocation);

   string base64;
   // ENCODE TO BASE 64
   base64 = Convert.ToBase64String(msRawData.GetBuffer(), 0, (int)msRawData.Length);

    return base64;
}

On the ASPX page you need to remove everything below the Page directive.
In the Page_Load event you need this code. Note you should NOT use Response.End - there is a known issue with it creating a Threading exception.  Use HttpContext.Current.ApplicationInstance.CompleteRequest() instead. 


Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.Charset = "";

// get results of merge as base 64 encoded stream
string strBase64 = ds.PreviewMerge(req);

// DECODE into memory stream
byte[] raw = Convert.FromBase64String(strBase64);

using (MemoryStream decoded = new MemoryStream(raw))
{
   // load the stream into the Response Output stream
   decoded.WriteTo(Response.OutputStream);
}
// set the content type for docx
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();