Configuring a trace listener

Mostly for my own reference, here’s an example of how to configure a trace listener that writes events to a text file:

<configuration>
  ...
  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <add name="TestTrace" type="System.Diagnostics.TextWriterTraceListener"
            initializeData="trace.txt" />
      </listeners>
    </trace>
   </system.diagnostics>
  ...
</configuration>

After that, all calls to System.Diagnostics.Trace.Write* functions are logged to the textfile. Example:

Trace.WriteLine("Something happened...");

Modifying log4net settings

Here’s how to modify log4net settings from a config file before initializing log4net. This can be useful for example if you have encrypted connection strings in App.config and have to decrypt them before passing them to log4net.

XmlElement log4NetSection = (XmlElement)ConfigurationManager.GetSection("log4net");

// Modify the xml, e.g. by adjusting the InnerXml property...
log4NetSection.InnerXml = StringCrypto.DecryptTaggedString(mypwd, log4NetSection.InnerXml);

XmlConfigurator.Configure(log4NetSection);

/Emil

Best practice for NHibernate: Don’t store references to ISession-instances

Do you see any (or all) of these exceptions (NHibernate.ADOException) in you NHibernate-based web application?

could not execute query
failed to lazily initialize a collection, no session or session was closed
Transaction not successfully started
The server failed to resume the transaction. Desc:86000002dc.

If you do, it might be a good idea to make sure you don’t store any ISession-references anywhere. If you do there’s a great risk that something goes wrong if two threads (i.e. two requests) are using the session at the same time as the NHibernate session implementation is not thread safe.

Instead, do this when an ISession is needed:

ISession session = SessionFactory.Factory.GetCurrentSession();

That code will get the session from the current context (which of course must be updated by your code, look here for more info).

/Emil

Distinct results sets with ICriteria in NHibernate

If you’re using ICriteria to write queries in NHibernate and you’re having citeria on items in a one-to-many relation (e.g. retrieve all departments with at least one employee named “John”) then you’re likely to have experienced that too many objects are returned (at least more than expected). In the example, all departments with multiple “Johns” will be returned once per matching employee. This is probably not what’s expected, so here’s how to fix it:

// The 'criteria' variable contains criteria on Department

// Create alias to use for employee conditions
ICriteria aliasCriteria = criteria.CreateAlias("Employees", "pr");

// Add criteria on the alias...

// We only want distinct root objects returned
criteria.SetResultTransformer(Transformers.DistinctRootEntity);

The trick is to use Transformers.DistinctRootEntity (at least in NHibernate 2.1). In earlier versions you may want to try something like this:

criteria.SetResultTransformer(new DistinctRootEntityResultTransformer());

Related to this is also how to write criteria that counts the results without actually retrieving them, which can be very useful for paging the result set (i.e. only read a subset of the results at a time). Normally you would write something like this:

criteria.SetProjection(Projections.Count("Id"));
int numberOfItems = (int)criteria.UniqueResult();

However, this suffers from the same problem as described above. To get the correct result, do this:

criteria.SetProjection(Projections.CountDistinct("Id"));
int numberOfItems = (int)criteria.UniqueResult();

Problem solved!

Credits to my colleague Jon for helping out with these rather obscure problem solutions!

/Emil

Working with .Net assemblies in PowerShell

It’s quite easy to load and use .Net assemblies in Windows PowerShell, which of course is one of the main selling points of the script language. In this post I’ll show a few examples.

Listing all loaded assemblies:

PS> [AppDomain]::CurrentDomain.GetAssemblies()

GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Conso...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c5619...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\System.Management.Automati...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\System.Configuration.Insta...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Comma...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Secur...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\Microsoft.PowerShell.Comma...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\System.Management\2.0.0.0_...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\System.DirectoryServices\2...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_32\System.Data\2.0.0.0__b77a5c5...
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c...

To load a new assembly, do the following:

[reflection.assembly]::loadfile('C:\...\bin\Entities.dll')

When an assembly has been loaded, types and code in it can be used with the new-object Cmdlet, for example:

PS> $obj = new-object Entities.Affär
PS> $obj.Affärsnummer = “2323232”
PS> $obj.Kundnamn = “Acme”
PS> $obj

Kundnummer :
Kundnamn : Acme
Organisationsnummer :
KontaktpersonKund :
Referens :
Affärsnummer : 2323232
Fas :
Betalningsvillkor :
Status : None
Fakturaaddress :
Fakturor : {}
Id : 0

PS> $obj.Validate()
Exception calling “Validate” with “0” argument(s): “Affärsnummer: Felaktig data: Affärsnummer skall alltid vara 9 siffror”
At line:1 char:14
+ $obj.Validate( < <<< ) [/code] Pretty cool, huh? (BTW, Validate() is a method on the "Affär" class.) Also note that some command completion works with imported types. For example, if I type "$obj." and then press TAB, then the console will cycle through the available members of the type in question. There's more to write about this interesting subject, so more posts will come... /Emil

hibernate.cfg.xml not found in ASP.Net application?

In later versions of NHibernate it’s possible to separate NHibernate configuration from normal application configuration (which is normally done in App.config/Web.config) by using a separate file called hibernate.cfg.xml.

Works great, but there is one thing to keep in mind in ASP.Net applications, namely that it should be placed in the bin/ folder, not in the root folder fo the application! Took me quite some time to figure out! So if you have the same problem as me, just move it to that folder 🙂

/Emil

Exclude files and folders from Web Deployment projects

Getting a lot of garbage when compiling web deployment projects? Don’t worry, there’s an easy remedy!

This is how to do it (using Visual Studio):

  1. Check out the web deployment project.
  2. Right-click the deployment project in the Solution Explorer and select “Open Project File”. The project file will be opened in the built-in Xml editor.
  3. Insert Xml code similar to the following last in the file, just before the end tag for the Project element:
    <project>
      ...
      <ItemGroup>
        <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\obj\**\*.*"/>
        <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\Properties\**\*.*"/>
        <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.csproj*"/>
        <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.resx"/>
        <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.Publish.xml"/>
      </ItemGroup>
    </project>
    
  4. After this change, all files matching the wildcard expressions given in ExcludeFromBuild elements will be excluded. Problem fixed!

Good luck deploying!

Update: The double asterisks means that the path matching is done recursively in the file system. For example, to exclude everything from the “obj\” folder we have to use “obj\**\*.*” since “obj\*.*” only matches files placed directly in the folder. Files in sub-folders would not be excluded if we used that pattern.

/Emil

Retrieving the computer domain namne

This is how to retrieve the full domain name of the current computer:

public static string GetFullComputerName()
{
  ManagementObject cs;
  using (cs = new ManagementObject("Win32_ComputerSystem.Name='" +
      Environment.MachineName + "'"))
  {
    cs.Get();
    return String.Format("{0}.{1}", Environment.MachineName, cs["domain"]);
  }
}

To make this work, you need a reference to System.Management.dll.

The result will be something like:

active99.i.activesolution.se

If anyone has a better approach, please leave a comment 🙂

/Emil