NUnit binary requirement...

I like to have all dependencies for a project in the source code repository so that the solution can build for new developers straight from a check out. After having to work sift through the NUnit binary (again) to work out what I needed rather than reference the GAC, this list is as a reminder:

nunit-console-runner.dll
nunit-console.exe
nunit-console.exe.config
nunit-gui-runner.dll
nunit.core.dll
nunit.core.extensions.dll
nunit.core.interfaces.dll
nunit.exe
nunit.exe.config
nunit.framework.dll
nunit.framework.extensions.dll
nunit.mocks.dll
nunit.uikit.dll
nunit.util.dll

 

This includes the UI stuff required for running the GUI - just in case there is someone out there that doesn't have resharper...

Messed up Service Factory projects!

Using service factory modelling edition recently I managed to get a new service project into a position where it would not generate code with no indication as to why.  I knew it was my fault as I had deleted, moved and renamed projects a few times - it was one of those situations where I wanted to get the name right, but had an indecisive few minutes changed it, then eventually settled on the first name.  The self inflicted mess I managed to end up with was two projects in the project mapping table, and obviously by sheer luck I had chosen the one that referenced the deleted projects!

I resolves it by opening the ProjectMapping.xml and finding the ProjectMappingTable element that contained the correct projects references - basically this meant finding by project Id those projects that still exist in the solution file.

Configuring logging for ODP.NET

For the 11g client it's just not documented very well. The details of the settings can be found in http://download.oracle.com/docs/html/E10927_01/featConfig.htm, but you need to set the values in HKLM/ORACLE/ODP.NET/CLR Version/.  To get started set the TraceLevel to 1 and TraceFileName to something sensible and you get the method entry and exit points along with SQL logged.

Although not a great deal of use for solving or working around some of the issues we have been encountering with the 11.1.0.6.20 provider version (particularly problematic are support for the user defined object types and XMLType), but useful for debugging and identifying issues.

Enterprise Library ValueValidator for NHibernate unique values

Mouthful of a title...  So the background story is the need to check uniqueness of a value against the existing database of values during the Enterprise Library (v4.0) validation (and before the DB constraint violation), oh and I'm using NHibernate (v2.0) for persistence.  I decided to to create a ValueValidator to allow the application of rule by config or attribute rather than implement as self validation. Straight to it then. I will list the full class later but to help explain the cocky bit these are the main methods:

private bool IsUnique(Type targetType, string propertyName, object propertyValue, object validatedObject)
{
    using (ISession session = m_factory.OpenSession())
    {
        int matches = (int)session.CreateCriteria(targetType)
            .Add(Expression.Eq(propertyName, propertyValue))
            .Add(Expression.Not(EqualIdentifier(targetType, validatedObject)))
            .SetProjection(Projections.ProjectionList()
                .Add(Projections.RowCount()))
            .UniqueResult();
        return (matches == 0);
    }
}
private SimpleExpression EqualIdentifier(Type targetType, object validatedObject)
{
    ISessionFactoryImplementor factoryImplementor = (ISessionFactoryImplementor)m_factory;
    IEntityPersister persister = factoryImplementor.GetEntityPersister(targetType);
    object idPropertyValue = persister.GetIdentifier(validatedObject, EntityMode.Poco);
    string idPropertyName = persister.IdentifierPropertyName;
    return Expression.Eq(idPropertyName, idPropertyValue);
}

The IsUnique method is called within the DoValidate of the ValueValidator, it constructs a criteria query to basically select count(*) from table where unique property = value and not key column = key value - with the identifier detail returned as a SimpleExpression from the  EqualIdentifier method using the objects persister.  Actually pretty simple and sweet!  Full code for the validator:

public class UniqueValueValidator : ValueValidator
{
    private ISessionFactory m_factory;
    public UniqueValueValidator(string messageTemplate, string tag, bool negated)
        : base(messageTemplate, tag, negated)
    {
        Configuration config = new Configuration().Configure();
        m_factory = config.BuildSessionFactory();
    }
    protected override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
    {
        Type targetType = GetTargetType(currentTarget);
        object target = GetTargetObject(currentTarget);
        if (!IsUnique(targetType, key, objectToValidate, target))
            validationResults.AddResult(new ValidationResult(key + " must be unique", currentTarget, key, base.Tag, this));
    }
    private bool IsUnique(Type targetType, string propertyName, object propertyValue, object validatedObject)
    {
        using (ISession session = m_factory.OpenSession())
        {
            int matches = (int)session.CreateCriteria(targetType)
                .Add(Expression.Eq(propertyName, propertyValue))
                .Add(Expression.Not(EqualIdentifier(targetType, validatedObject)))
                .SetProjection(Projections.ProjectionList()
                    .Add(Projections.RowCount()))
                .UniqueResult();
            return (matches == 0);
        }
    }
    private SimpleExpression EqualIdentifier(Type targetType, object validatedObject)
    {
        ISessionFactoryImplementor factoryImplementor = (ISessionFactoryImplementor)m_factory;
        IEntityPersister persister = factoryImplementor.GetEntityPersister(targetType);
        object idPropertyValue = persister.GetIdentifier(validatedObject, EntityMode.Poco);
        string idPropertyName = persister.IdentifierPropertyName;
        return Expression.Eq(idPropertyName, idPropertyValue);
    }
    private Type GetTargetType(object currentTarget)
    {
        if (currentTarget == null)
            throw new ArgumentException("Target should not be null");
        if (currentTarget is IValidationIntegrationProxy)
            return (currentTarget as IValidationIntegrationProxy).ValidatedType;
        return currentTarget.GetType();
    }
    private object GetTargetObject(object currentTarget)
    {
        if (currentTarget == null)
            throw new ArgumentException("Target should not be null");
        if (currentTarget is IValidationIntegrationProxy)
            return (currentTarget as IValidationIntegrationProxy).GetRawValue();
        return currentTarget;
    }
    protected override string DefaultNegatedMessageTemplate
    {
        get { return "The value must be unique"; }
    }
    protected override string DefaultNonNegatedMessageTemplate
    {
        get { return "The value must be unique"; }
    }
}

and for the attribute:

public class UniqueValueValidatorAttribute : ValueValidatorAttribute
{
    protected override Validator DoCreateValidator(Type targetType)
    {
        return new UniqueValueValidator(MessageTemplate, Tag, Negated);
    }
}
Which means that you can add the UniqueValueValidator attribute to properties to enforce unique value during validation:
        [UniqueValueValidator]
        public virtual string Name { get; set; }

HttpContext in ASP.NET MVC

A quick gotcha for ASP.NET MVC newbies (like me), the HttpContext object on the Controller are not set on construction.  I found this out when trying to capture the authenticated users IIdentity and then spent 20 minutes of my life working out why it was always null!  Good excuse to peruse the source though...

Move Service Factory projects

I have been using the Service Factory Modelling Edition a lot lately, whilst they do produce a lot of projects and may appear unwieldy at first glance, the consistency of implementation alone is worth it in some of the larger implementations.

The issue I have was a need to migrate a Service Factory project into another pre-existing solution (I am a tidy freak).  The best way I found was to manually copy and edit the sln file; all you need to do is copy the ProjectMapping.xml file along with the folders containing the implementations and the models into the new solution folder, then start to edit the new sln file!  As a word of caution remember to make copies of everything before you start, as editing the sln file can lead to splinters...

In the project section for Solution Items add the ProjectMapping.xml file:

Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A47C9E2B-340A-4E76-AA04-EE0B0A053637}"
            ProjectSection(SolutionItems) = preProject
                        ProjectMapping.xml = ProjectMapping.xml
            EndProjectSection
EndProject

 

If there is no Solution Items section just add a file to the root of the new solution in the IDE then close and start again.

The copy all of the Project sections relating the the folders and projects for the Service Factory from the original solution into the new solution, they are quite nicely grouped together, so this should be easy.  Just be wary to ensure that any other projects added to the original solution are not copied across.

Finally (to make sure you get all the solution folders setup correctly) copy all of the relevant nesting from the section:

GlobalSection(NestedProjects) = preSolution

 

This is usually at the end of the sln file, to the same location in the new sln.  If you have done everything correctly (some luck required) the new solution should open and build correctly in the IDE!  If the solution won't build and moans about project references, the most likely cause is the NestedProjects section. This section is sensitive to non existent projects, which is easy to do if If you are moving the project from a solution that contains more than just the Service Factory. Check each identifier in the section exists in the solution file, removing all those that don't.

Type descriptors for extensibility...

The TypeDescriptor architecture is used widely within the .NET framework and allows for greater extensibility than reflection.  The msdn article Type Descriptor Overview gives a good introduction to the model.  Whilst most often used in the design time environment, there are notable examples where used at runtime (such as the PropertyGrid), and having the capability to affect the TypeDescriptor at runtime opens up many possibilities.

In this example I will walk through a simple example to demonstrate how easy it is to override the TypeDescriptor at runtime to affect display and edit capability with the PropertyGrid.  The example will be to add a TypeDescriptor to a simple data type to demonstrate how the ReadOnly attribute can be changed based on runtime state.

In this example I use a simple data item type within the application:

    public partial class DataItem
    {
        [Description("Name of the data item")]
        [ReadOnly(true)]
        public string Name { get; set;}
        [Description("Data item description")]
        public string Description { get; set; }
        [Browsable(false)]
        public bool ReadOnly { get; set; }
    }

The Name property is decorated with a ReadOnly attribute and Description is editable, for simplicity I have included a ReadOnly property within the aim being that when set to ReadOnly the Description property will also be made ReadOnly.

The code below defines the TypeDescriptor infrastructure required.

    public class DataItemDescriptionProvider : TypeDescriptionProvider
    {
        public DataItemDescriptionProvider() : this(TypeDescriptor.GetProvider(typeof(DataItem)))
        {
        }
        public DataItemDescriptionProvider(TypeDescriptionProvider parent): base(parent)
        {
        }
        public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
        {
            ICustomTypeDescriptor td = base.GetTypeDescriptor(objectType, instance);
            td = new DataItemTypeDescriptor(td, instance as DataItem);
            return td;
        }
    }
    public class DataItemTypeDescriptor : CustomTypeDescriptor
    {
        private DataItem m_instance;
        public DataItemTypeDescriptor(ICustomTypeDescriptor parent, DataItem instance) : base(parent)
        {
            m_instance = instance;
        }
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            PropertyDescriptorCollection properties = base.GetProperties(attributes);
            if (m_instance.ReadOnly)
            {
                var p = from property in properties.OfType<PropertyDescriptor>()
                        select new DataItemReadOnlyPropertyDescriptor(true, property);
                return new PropertyDescriptorCollection(p.ToArray());
            }
            return properties;
        }
    }
    public class DataItemReadOnlyPropertyDescriptor: PropertyDescriptor
    {
        private PropertyDescriptor m_descriptor;
        private bool m_readOnly;
        
        public DataItemReadOnlyPropertyDescriptor(bool readOnly, PropertyDescriptor descriptor) : base(descriptor)
        {
            m_descriptor = descriptor;
            m_readOnly = readOnly;
        }
        #region abstract PropertyDescriptor implementation
        public override bool CanResetValue(object component)
        {
            return m_descriptor.CanResetValue(component);
        }
        public override Type ComponentType
        {
            get { return m_descriptor.ComponentType; }
        }
        public override object GetValue(object component)
        {
            return m_descriptor.GetValue(component);
        }
        public override bool IsReadOnly
        {
            get { return m_readOnly; }
        }
        public override Type PropertyType
        {
            get { return m_descriptor.PropertyType; }
        }
        public override void ResetValue(object component)
        {
            m_descriptor.ResetValue(component);
        }
        public override void SetValue(object component, object value)
        {
            m_descriptor.SetValue(component, value);
        }
        public override bool ShouldSerializeValue(object component)
        {
            return m_descriptor.ShouldSerializeValue(component);
        }
        #endregion
    }

The DataItemDescriptionProvider implements TypeDescriptionProvider overriding the GetTypeDescriptor method to return the specialized TypeProvider, later in our calling code we will hand this to TypeProvider for our Type.

The DataItemTypeDescriptor is where the work happens, implementing the CustomTypeDescriptor which is a simple default implementation of the ICustomTypeProvider (minimising the amount of typing required).  We are overriding the GetProperties method and extracting the properties from the base class, then (if the instance ReadOnly  is set) creating overridden PropertyDescriptors (which simply allows the setting of the ReadOnly attribute and includes lots of boiler plate to hand off to the base instance for all other requests). 

In usage code we need to hand the TypeDescriptionProvider to TypeProvider:

            TypeDescriptor.AddProvider(new DataItemDescriptionProvider(), typeof(DataItem));

To demonstrate I used this within a simple windows forms app with a property grid, show below:

 read EDITABLE

You can download the sample app here.

Visual Studio 2008 troubleshoot start-up

Had a monstrously weird issue with a particular solution file, everything was working fine (opening, building, compiling and even unit testing) then for seemingly no apparent reason the solution wouldn't even open!

I didn't know this was possible but looking at the devenv.exe command line arguments it is possible to log all activity using the /Log switch.  This outputs to an XML file which outputs with an xml-stylesheet processing instruction href to ActivityLog.xsl. Quick scan of the disk didn't find the file, but I found a post on the MSDN forum with the following:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:output method="html"  encoding="utf-16"/>
    <xsl:template match="activity">
        <head>
        <title>Activity Monitor Log</title>
        <style type="text/css">
            body{ text-align: left; width: 100%;  font-family: Verdana, sans-serif; }
            table{ border: none;  border-collapse: separate;  width: 100%; }
            tr.title td{ font-size: 24px;  font-weight: bold; }
            th{ background: #d0d0d0;  font-weight: bold;  font-size: 10pt;  text-align: left; }
            tr{ background: #eeeeee}
            td, th{ font-size: 8pt;  padding: 1px;  border: none; }
            tr.info td{}
            tr.warning td{background-color:yellow;color:black}
            tr.error td{background-color:red;color:black}
           
            span {text-decoration:underline}
            a:hover{text-transform:uppercase;color: #9090F0;}
        </style>
        </head>
        <body>     
        <table>
            <tr class="title">
                <td colspan="7">Activity Monitor Log</td>
            </tr>            
            <tr>
                <td colspan="2">infos</td>
                <td colspan="5"><xsl:value-of select="count(entry[type='Information'])"/></td>
            </tr>
            <tr>
                <td colspan="2">warnings</td>
                <td colspan="5"><xsl:value-of select="count(entry[type='Warning'])"/></td>
            </tr>
            <tr>
                <td colspan="2">errors</td>
                <td colspan="5"><xsl:value-of select="count(entry[type='Error'])"/></td>
            </tr>
            <tr>
                <th width="20">#</th>
                <th width="50">Type</th>
                <th>Description</th>
                <th width="280">GUID</th>
                <th>Hr</th>               
                <th>Source</th>
                <th>Time</th>
            </tr>              
            <xsl:apply-templates/>
        </table>
        </body>
    </xsl:template>
    <xsl:template match="entry">
        <!-- example
       
          <entry>
            <record>136</record>
            <time>2004/02/26 00:42:59.706</time>
            <type>Error</type>
            <source>Microsoft Visual Studio</source>
            <description>Loading UI library</description>
            <guid>{00000000-0000-0000-0000-000000000000}</guid>
            <hr>800a006f</hr>
            <path></path>
        </entry>
       
        -->
        <xsl:choose>
            <xsl:when test="type='Information'">
                    <tr id="info" class="info">
                        <td><xsl:value-of select="record"/></td>
                        <td></td>
                        <xsl:call-template name="row"/>
                    </tr>               
            </xsl:when>               
            <xsl:when test="type='Warning'">
                    <tr id="warning" class="warning">
                        <td><xsl:value-of select="record"/></td>
                        <td>Warning</td>
                        <xsl:call-template name="row"/>
                    </tr>               
            </xsl:when>            
            <xsl:when test="type='Error'">
                    <tr id="error" class="error">
                        <td><xsl:value-of select="record"/></td>
                        <td>ERROR</td>
                        <xsl:call-template name="row"/>
                    </tr>               
            </xsl:when>               
        </xsl:choose> 
    </xsl:template>
   
    <xsl:template name="row">
                <td id="description"><xsl:value-of select="description"/><xsl:if test="path"><br/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<xsl:value-of select="path"/></xsl:if></td>
                <td id="guid"><xsl:value-of select="guid"/></td>   
                <td id="hr"><xsl:value-of select="hr"/></td>   
                <td><xsl:value-of select="source"/></td>   
                <td><xsl:value-of select="time"/></td>
    </xsl:template>           
</xsl:stylesheet>

The log should point to issues, but in this case actually didn't finish writing - had to close the activity to make it valid!  Using the /SafeMode switch the solution opened which pointed to a bogus add-in.  That will teach me to install so many random tools to make me more efficient!

Fix port for development server in VS2008 web projects

From time to time you need to define a fixed port for web projects during development, an example may be a need to exercise services through a proxy for testing.  This can be fixed in the SLN file to ensure that for all solution users the port is fixed.

Find the project in the SLN, and VWDPort setting will be defined, set the required port and add VWDDynamicPort = "false" setting below.  once reloaded the solution will always use the chosen port.

Policy injection and logging with the Enterprise Library

There are plenty of articles out there that cover use of the logging block with enterprise library, try here and here. For method invoke logging most end up with attribute markers for logging like the LogCallHandler. Whilst this removes most of the burdensome log code within methods - it still looks ugly IMHO, and requires compilation to change log output. For example the following basic class:

    public class Foo: MarshalByRefObject
    {
        [LogCallHandler(IncludeParameters = true, 
            Categories = new[] { "General" },
            LogAfterCall = true, LogBeforeCall = true, 
            Priority = 4, Severity = TraceEventType.Verbose)]
        public void Bar(string param)
        {
            Console.WriteLine(param);
        }
    }

With configuration:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </configSections>
  <loggingConfiguration name="Logging Application Block" tracingEnabled="true"
    defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
    <listeners>
      <add fileName="trace.log" header="----------------------------------------"
        footer="----------------------------------------" formatter="Text Formatter"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="FlatFile TraceListener" />
    </listeners>
    <formatters>
      <add template="Timestamp: {timestamp}&#xD;&#xA;Type Name: {property(TypeName)}&#xD;&#xA;Method Name: {property(MethodName)}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
        type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="Text Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="FlatFile TraceListener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category" />
      <errors switchValue="All" name="Logging Errors &amp; Warnings" />
    </specialSources>
  </loggingConfiguration>
</configuration>

gives log output:

----------------------------------------
Timestamp: 10/09/2008 12:11:40
Type Name: EntLibLogging.Foo
Method Name: Bar
Extended Properties: param - log test

----------------------------------------
----------------------------------------
Timestamp: 10/09/2008 12:11:40
Type Name: EntLibLogging.Foo
Method Name: Bar
Extended Properties: param - log test

----------------------------------------

When called using:

            Foo f = PolicyInjection.Create<Foo>();
            f.Bar("log test");

Items to note are the change in the text formatter template to use the {property()} syntax to pick up the extended properties as defined in this MSDN article.

With a pretty simple change in Config the LogCallHandler attribute can be removed from the code and logging become more flexible.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </configSections>
  <policyInjection>
    <policies>
      <add name="Policy">
        <matchingRules>
          <add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MemberNameMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            name="Member Name Matching Rule">
            <matches>
              <add match="Bar" ignoreCase="false" />
            </matches>
          </add>
        </matchingRules>
        <handlers>
          <add logBehavior="BeforeAndAfter" beforeMessage="Before" afterMessage="After"
            eventId="0" includeParameterValues="true" includeCallStack="false"
            includeCallTime="true" priority="-1" severity="Information"
            type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.LogCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            name="Logging Handler">
            <categories>
              <add name="General" />
            </categories>
          </add>
        </handlers>
      </add>
    </policies>
  </policyInjection>
  <loggingConfiguration name="Logging Application Block" tracingEnabled="true"
    defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
    <listeners>
      <add fileName="trace.log" header="----------------------------------------"
        footer="----------------------------------------" formatter="Text Formatter"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="FlatFile TraceListener" />
    </listeners>
    <formatters>
      <add template="Timestamp: {timestamp}&#xD;&#xA;Type Name: {property(TypeName)}&#xD;&#xA;Method Name: {property(MethodName)}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
        type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="Text Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="FlatFile TraceListener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category" />
      <errors switchValue="All" name="Logging Errors &amp; Warnings" />
    </specialSources>
  </loggingConfiguration>
</configuration>

note the introduction of the policy injection with a specific match for the method name "Bar". 

Once you have taken the step of inheriting from MarshallByRef and instantiating using PolicyInjection to allow policy based logging, stepping onto the config based approach gives a very flexible logging capability.