Upgrading PostSharp from ver 2.1 to new version 3.0

I was upgrading our solutions from PostSharp 2 to PostSharp 3. The small solution based on cache attribute from http://cache.codeplex.com/ was upgraded without any problems.

Upgrading my main solution by installing nuget package PostSharp also was quite well. 
The only annoying thing was that installer added 
RequiresPostsharp.cs file to all projects, that already had SkipPostSharp=true setting and I had manually remove them
The issue was reported at
but Gael unfortunately  considers this behavior “by design“.
 
More work was to convert   psproj files PostSharp.Toolkit.Diagnostics ver 2.1 to new PostSharp.Patterns.Diagnostics.3.0.
There was no documentation.I’ve only found a short notice at the bottom of 

PostSharp Toolkits 2.1 need to be uninstalled using NuGet. Instead, you can install PostSharp Pattern Libraries 3 from NuGet. 
Namespaces and some type names have changed. 
Uninstall for 2.1 suggested to remove NLog nuget package, which we are using regardless of PostSharp.
I’ve run 
Install-Package PostSharp.Patterns.Diagnostics.NLog

The install of PostSharp.Patterns.Diagnostics.NLog didn’t like the latest version of Nlog, but Gael 
fixed it recently(http://support.sharpcrafters.com/discussions/problems/1211-nlog-weaver-version-error).
The installs haven't changed the content of PSPROJ files and I had  to manually update them.
1. Deleted old references to DLL and inserted dg:LoggingProfiles profile element
 
 <!–<Using File=”……..packagesPostSharp.Toolkit.Diagnostics.NLog.2.1.1.12toolsPostSharp.Toolkit.Diagnostics.Weaver.NLog.dll”/>
  <Using File=”……..packagesPostSharp.Toolkit.Diagnostics.2.1.1.12toolsPostSharp.Toolkit.Diagnostics.Weaver.dll” /> –>
2. After advise from Gael  I’ve  removed the Task element and change <Data Name="XmlMulticast">into simply <Multicast>.
3. I’ve also replaced namespace and DLL names in LogAttribute xmlns properties to be “clr-namespace:PostSharp.Patterns.Diagnostics;assembly:PostSharp.Patterns.Diagnostics”
The psproj file becomes similar to the following and seemed to work.
<?xml version=”1.0″ encoding=”utf-8″?>
< Project xmlns=”http://support.sharpcrafters.com/discussions/problems/1264/r?go=aHR0cDovL3N1cHBvcnQuc2hhcnBjcmFmdGVycy5jb20vZGlzY3Vzc2lvbnMvcHJvYmxlbXMvMTI2NC9yP2dvPWFIUjBjRG92TDNOMWNIQnZjblF1YzJoaGNuQmpjbUZtZEdWeWN5NWpiMjB2WkdselkzVnpjMmx2Ym5NdmNISnZZbXhsYlhNdk1USTJOQzl5UDJkdlBXRklVakJqUkc5MlRETk9hbUZIVm5SWldFMTFZMGM1ZW1SSVRtOVpXRXAzVEcwNWVWcDVPSGhNYWtGMldUSTVkVnB0Ykc1a1dFcG9aRWRzZG1KcFduaGtWemt3; xmlns:dg=”clr-namespace:PostSharp.Patterns.Diagnostics;assembly:PostSharp.Patterns.Diagnostics”>
  <Property Name=”LoggingBackEnd” Value=”nlog” />
  <Using File=”..packagesPostSharp.Patterns.Diagnostics.3.0.26toolsPostSharp.Patterns.Diagnostics.Weaver.dll” />
  <Using File=”..packagesPostSharp.Patterns.Diagnostics.NLog.3.0.26toolsPostSharp.Patterns.Diagnostics.Weaver.NLog.dll” />
  <dg:LoggingProfiles>
    <dg:LoggingProfile Name=”Exceptions” OnExceptionOptions=”IncludeParameterType | IncludeParameterName | IncludeParameterValue | IncludeThisArgument” OnEntryLevel=”None” OnSuccessLevel=”None” />
  </dg:LoggingProfiles>
  <Multicast>

      <LogAttribute xmlns=”clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics” AttributeTargetAssemblies=”Applications.MyApp” AttributeTargetTypes=” Applications.MyApp.MyCustomer” AttributeTargetMembers=”*” OnExceptionLevel=”Warning” OnExceptionOptions=”IncludeParameterValue” />

   </Multicast>

</Project>

It was deployed to CI test environment, where we noticed delays and timeouts. I found that despite that only  OnExceptionLevel and  OnExceptionOptions were specified, the new LogAttribute generated verbose trace information, which caused severe performance hit.

4. I had to change LogAttribute to LogExceptionAttribute and remove OnExceptionLevel and OnExceptionOption properties.
 

AddIfNotNull collection extensions

I want to post a few recently created collection extensions to write in one line, what otherwise takes 2 or more
     public static void AddIfNotNull( this IList coll, T newItem) where T : class
        {
            if (newItem != null)
            {
                coll.Add(newItem);
            }
        }

         public static void AddRangeIfNotNullOrEmpty( this List coll, IEnumerable newItems) where T : class
        {
            if (!newItems.IsNullOrEmptySequence())
            {
                coll.AddRange(newItems);
            }
        }
 
  public static void AddIfNotContains( this Dictionary dictionary,  TKey key, TValue value)
        {
            if (!dictionary.ContainsKey(key))
            {
                dictionary.Add(key, value);
            }
        }
The methods use
public static bool IsNullOrEmptySequence(this IEnumerable c)
              {
                      return (c == null || !c.Any() );
              }

I’ve also found a few extensions, that could be useful in https://pikacode.com/Barankin/Fabrika-dveri/file/default/CRM/Common/Extensions/LinqExtensions.cs

EntLIb editor corrupts config files

I’ve tried to use Microsoft Enterprise Library(EntLIb) editor, as it was suggested in http://weblogs.asp.net/sukumarraju/archive/2011/11/07/configuring-wcf-service-to-utilise-enterprise-library-logging-application-to-log-data-to-database.aspx, but after changes all comments in config files were removed.
 
Always consider to move any Enterprise Library configurations to a separate file before editing.

Using PostSharp.Toolkit.Diagnostics, when not all developers have Pro licenses.

We have only couple of developers who are using PostSharp.Toolkit.Diagnostics and having  PostSharp Pro license .
However ther are much more developers , who are building our solution, but do not required Toolkit.Diagnostics XmlMulticast features, that are referred in %ProjName%.psproj file.
 
As a workaround I’ve suggested to to replace locally psproj file with dummy, that doesn’t have XmlMulticast(PostSharp feature that available only in Pro edition).
 
If a developer doesn’t have PostSharp Pro license, they shoul set  Environment variable POSTSHARP_PRO=false to effectively exclude psproj from the build on their local machine.
detailed instructions How to Add, Remove or Edit Environment variables in Windows 7 can be found at http://www.itechtalk.com/thread3595.html.
For Each project using PostSharp.Toolkit.Diagnostics  A subfolder PreBuild.Config has been created.
It includes minimal dev.psproj ,
PreBuild.cmd
and the full %ProjName%.psproj (e.g. MyProject.psproj ) that is used as a master version on build machine.
 

%ProjName%.psproj located in the root on local machines should kept  as minimal – the same as Dev.Configdev.psproj.

 

<?xml version=1.0 encoding=utf-8?>

<!–Empty project for developers without PostSharp Pro.

       Do NOT check-in   %ProjName%.psproj

       –>

       <Project xmlns=http://schemas.postsharp.org/1.0/configuration ReferenceDirectory={$ReferenceDirectory}>

              <Property Name=LoggingBackEnd Value=nlog />

              <Using File=default />

              <Tasks>

              </Tasks>

 

</Project>
 
For each project that uses toolkit insert into PreBuild Event command line
cmd /c $(ProjectDir)PreBuild.Config\PreBuild.cmd $(ProjectName)
 
File PreBuild.Config\PreBuild.cmd
rem developers without PostSharp Pro installed please set environment variable POSTSHARP_PRO=false
@rem see http://www.itechtalk.com/thread3595.html detailed instructions How to Add, Remove or Edit Environment variables in Windows 7
@REM Insert into PreBuild Event command line
@REM cmd /c $(ProjectDir)\PreBuild.Config\PreBuild.cmd $(ProjectName)
set ProjName=%1
set PSProjFile=..\%ProjName%.psproj
if ‘%POSTSHARP_PRO%==’false goto devPsproj
:proPsproj
set FileFrom=%ProjName%.psproj
@goto copyFile
:devPsproj
set FileFrom=dev.psproj
:copyFile
rem The current dir seems to be \bin\Debug for Library project or Windows Console/Services, but \bin for WAP(Webjet application project)
cd ..\..\PreBuild.Config

ATTRIB -R %PSProjFile%
copy /Y %FileFrom% %PSProjFile%
:end
@rem pause

VAB ValidationResults Extensions

We’ve started to actively used Microsoft Enterprise Library Validation Enterprise Block ( VAB)  and I was surprised , that a few commonly used  operations are not supplied(or I haven’t found them) out of the box.
See two extensions, that make use of VAB simpler

public static class ValidationResultsExtensions
       {
               public static string CombinedMessage( this ValidationResults results)
              {
                      string errorMessage = ( from res in results select String.Format( {0}:{1} , res.Key, res.Message)).ToDelimitedString( “;”);
                      return errorMessage;
              }
//Throws ValidationException if not valid
           public static void ValidateConstraints<T>( this T target)
           {
               Validator validator = ValidationFactory.CreateValidator<T>();
               var results = new ValidationResults();
               validator.Validate(target, results);
               if (results.IsValid == false)
               {
                   var errorMessage = results.CombinedMessage();
                   throw new ValidationException(errorMessage);
               }
           }
       }v

Using CollectionNotEmptyValidator

We recently started to use Microsoft Enterprise Library Validation Enterprise Block
 (VAB) to check interfaces between modules. One of the properties to validate is array  
of values, that should be not empty, and shoul include one of expected values. 
I found CollectionNotEmptyValidator at http://www.eggheadcafe.com/tutorials/xaml/9af7ac1a-d7f3-4e00-9aec-33ef1ec7d1a3/wpf-custom-validation-using-the-enterprise-library.aspx, that allows to  validate the property to satisfy part of requirements.
 
The class mostly works as is, I’ve only changed exception to Validation error if object to validate is not a collection.

        public override void DoValidate( object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
              {

                      if (objectToValidate is ICollection)
                     {

                            if (((( ICollection)objectToValidate).Count != 0) == Negated)
                           {

                                  LogValidationResult(validationResults, MessageTemplate, currentTarget, key);

                           }

                     }

                     else
                     {

//throw new ApplicationException(“Object type not supported by validator.”);

                           LogValidationResult(validationResults, “Object is not a collection.”, currentTarget, key);
                     }

I’ve also changed testing console app to a number of MSTest TestMethods.

#region Namespace Imports
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.SDC.Common.Validation;
using Microsoft.SDC.Common.Web.WCF;
using Microsoft.VisualStudio.TestTools.UnitTesting;


#endregion
namespace MSCommon.Validation
{
// I have defined an Order class
//      containing an Items property for this. There are four possible cases we can test:


//- Collection is empty, Negated property is false

//- Collection is non-empty, Negated property is false

//- Collection is non-empty, Negated property is true

//- Collection is empty, Negated property is true


       [ TestClass]
        public class CollectionNotEmptyValidatorTests
       {
              [ TestMethod(), CITest ]
               public void InvalidTest_CollectionIsEmpty()
              {
                      // Invalid test – collection is empty
                      var order = NewEmptyOrder();
                      CollectionNotEmptyValidator validator = new CollectionNotEmptyValidator ();

                      ValidationResults results = validator.Validate(order.Items);

                      Assert.IsTrue(!results.IsValid);

                      Debug.WriteLine(results.First().Message);

                      // Output: The collection must not be empty.
              }

              [ TestMethod(), CITest ]
               public void ValidTest_CollectionIsEmpty()
              {
                      // Valid test – collection has items
                      var order = NewEmptyOrder();
                     order.Items.Add( new Order. OrderItem());
                      CollectionNotEmptyValidator validator = new CollectionNotEmptyValidator ();

                      ValidationResults results = validator.Validate(order.Items);

                      Assert.IsTrue(results.IsValid);

                      Debug.WriteLine( “Valid test”);
              }

              [ TestMethod(), CITest ]
               public void NegatedTest_CollectionHasItems()
              {
                      // Invalid test – collection has items (Negated)

                      var order = NewEmptyOrder();
                     order.Items.Add( new Order. OrderItem());
                      CollectionNotEmptyValidator validator = new CollectionNotEmptyValidator (true );

                      ValidationResults results = validator.Validate(order.Items);
                      Assert.IsTrue(!results.IsValid);

                      Debug.WriteLine(results.First().Message);
                      // Output: The collection must be empty.
              }


              [ TestMethod(), CITest ]
               public void NegatedTest_CollectionIsEmpty()
              {
                      // Invalid test – collection has items (Negated)

                      var order = NewEmptyOrder();
                     order.Items.Add( new Order. OrderItem());
                      CollectionNotEmptyValidator validator = new CollectionNotEmptyValidator (true );

                      ValidationResults results = validator.Validate(order.Items);
                      Assert.IsTrue(!results.IsValid);

                      Debug.WriteLine(results.First().Message);
                      // Output: The collection must be empty.
              }


               private static Order NewEmptyOrder()
              {
                      Order order = new Order();

                     order.Items = new Collection<Order .OrderItem >();
                      return order;
              }

              #region Example Classes for tests

               public class Order

              {
                     [ CollectionNotEmptyValidator()]
                      public Collection<OrderItem > Items { get; set; }

                      public bool ValidateItems()

                     {
                            return Items.Count > 0;
                     }

                      public class OrderItem

                     {
                     }
              }

              #endregion //Example Classes for tests
       }
}
I am planning to create derived ContainValidator.

Do NOT Change “Copy Local” project references to false, unless understand subsequences.

To optimize performance of visual studio build I’ve found multiple recommendations to change CopyLocal property for dependent dlls to false,
  1. e.g. From http://stackoverflow.com/questions/690033/best-practices-for-large-solutions-in-visual-studio-2008 
  • CopyLocal? For sure turn this off
  • Always set the Copy Local property to false and enforce this via a custom msbuild step
  • My advice is to always set ‘Copy Local’ to false

Some time ago we’ve tried to change the setting to false, and found that it causes problem for deployment of top-level projects.

Recently I’ve followed the suggestion and changed the settings for middle-level projects. It didn’t cause immediate issues, but I was warned by Readify consultant colin savage about possible errors during deployments.

I haven’t undone the changes immediately and we found a few issues during testing.

There are many scenarios, when you need to have Copy Local’ left to True.

The concerns are highlighted in some StackOverflow answers, but they have small number of votes.The concerns are highlighted in some StackOverflow answers, but they have small number of votes.

 

Top-level projects:  set copy local = true.

First of all, it doesn’t work correctly for top-level projects, i.e. executables or web sites.
  • for all the references in the one at the top set copy local = true.
If you set ‘ Copy Local = false’, VS will, unless you tell it otherwise, place each assembly alone in its own .binDebugdirectory. Because of this, you will need to configure VS to place assemblies together in the same directory. To do so, for each VS project, go to VS > Project Properties > Build tab > Output path, and set the Ouput path to ..binDebugfor debug configuration, and ..binRelease for release configuration.
 

Second-level  dependencies:  set copy local = true.

 

Another example when copylocal =false fails on run-time, is when top level assembly doesn’t directly referenced one of indirect dependencies.
E..g. Top-level assembly A has reference to assembly B with copylocal =true, but assembly B has reference to assembly C with copylocal =false. Most likely assembly C will be missing on runtime and will cause errors 

 
Copy local is important for deployment scenarios and tools. As a general rule you should use CopyLocal=True

and http://stackoverflow.com/questions/602765/when-should-copy-local-be-set-to-true-and-when-should-it-not?lq=1

Unfortunately there are some quirks and CopyLocal won’t necessary work as expected for assembly references in secondary assemblies structured as shown below.

  • MainApp.exe
  • MyLibrary.dll
  • ThirdPartyLibrary.dll (if in the GAC CopyLocal won’t copy to MainApp bin folder)

This makes xcopy deployments difficult . 

Reflection called DLLs  dependencies:  set copy local = true.
E.g user can see error “ISystem.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.”

The fix for the issue is recommended in http://stackoverflow.com/a/6200173/52277

“I solved this issue by setting the Copy Local attribute of my project’s references to true.”

 

In general, the problems with investigation of deployment issues may overweight the benefits of reduced build time.
Setting the Copy Local to false without considering deployment issues is not a good idea.