Monday, 8 February 2016

NHibernate & System.Transactions.TransactionException : The operation is not valid for the state of the transaction.


Problem

I haven't come across this in a while as my environments have been quite stable over the last couple of years. Then we got a brand spanking new clean Team City environment and I migrated our build/test/package tasks across, and suddenly half the test fail.

I started with the usual googles but didn't find anything useful. Then a light bulb went on, and I remembered I added something about this to my personal wiki. Maybe someone will stumble upon it and find it helpful.

Solution 


  1. Remote into the Server where your MSSQL server is running.
  2. Open Component Services. (click Start . In the search box, type dcomcnfg , and then press ENTER)
  3. In the Component Services snap-in, double-click Computers , Computer Name , and Distributed Transaction Coordinator .
  4. Right-click Local DTC , click Properties , and then click the Security tab.
  5. Under Security Settings , select the Network DTC Access check box.
  6. Under Transaction Manager Communication , select the Allow Inbound and Allow Outbound check boxes.Select an authentication level for DTC communications. Mutual Authentication Required is the most secure level. 
  7. Click Enable SNA LU 6.2 Transactions to deselect it.
  8. Continue to configure security settings, or click OK .




Full Error

System.Exception : Exception in constructor: System.Transactions.TransactionException: The operation is not valid for the state of the transaction.
   at System.Transactions.TransactionState.EnlistVolatile(InternalTransaction tx, IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction)
   at System.Transactions.Transaction.EnlistVolatile(IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions)
   at NHibernate.Impl.AbstractSessionImpl.CheckAndUpdateSessionStatus()
   at NHibernate.Impl.SessionImpl..ctor(IDbConnection connection, SessionFactoryImpl factory, Boolean autoclose, Int64 timestamp, IInterceptor interceptor, EntityMode entityMode, Boolean flushBeforeCompletionEnabled, Boolean autoCloseSessionEnabled, ConnectionReleaseMode connectionReleaseMode)
   at NHibernate.Impl.SessionFactoryImpl.OpenSession(IDbConnection connection, Boolean autoClose, Int64 timestamp, IInterceptor sessionLocalInterceptor)
   at NHibernate.Impl.SessionFactoryImpl.OpenSession(IInterceptor sessionLocalInterceptor)

Tuesday, 5 January 2016

Cut and paste logging (for dummies)


Disclaimer: 

  1. This is not great code but is super simple to use
  2. Performance was not a consideration
  3. I don't claim to have written it, just cobbled the bits together into one atomic class
  4. Lots of my blogs don't make it publish for the typical fear of retribution as it's not up to standard (I'd probably ridicule my own code in a week) So this time I'm sticking it out there warts and all and will update it as time goes by to make the blurb and code cleaner

Summary

Cut and paste this into a class in your project then start writing to Logger instead of Console and you get Console/File/UDP (Log2Console) output.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Reflection;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Threading;

namespace My.Project.Namespace
{
        public static class Logger
    {
        public enum LogLevel {Error,Warning,Info,Degug}        
        
        private static Socket _socket;
        private static IPEndPoint _socketEndPoint;
        private static FileStream _logFileStream;        
        
        private static readonly string logFile = string.Format("{0}/{1}.log", AppDomain.CurrentDomain.BaseDirectory, Assembly.GetExecutingAssembly().GetName().Name);
        private const int MaxFileSizeInMb = 10;
        private static readonly IPAddress UdpIp = IPAddress.Loopback;
        private const int UdpPort = 7071;
        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        
        static Logger()
        {
            Level = LogLevel.Info;
            InitialiseShutdownAction();
            InitialiseFileWriter();
            InitialiseUdpBroadcaster();
            InitialiseTraceListeners();
        }

        public static LogLevel Level { get; set; }

        public static void Error(string message)
        {
            WriteEntry("error", message);
        }

        public static void Error(Exception ex)
        {
            WriteEntry("error", ex.Message);
        }

        public static void Warning(string message)
        {
            if(Level < LogLevel.Warning) return;
            WriteEntry("warning", message);
        }

        public static void Info(string message)
        {
            if (Level < LogLevel.Info) return;
            WriteEntry("info", message);
        }

        public static void Debug(string message)
        {
            if (Level < LogLevel.Degug) return;
            WriteEntry("debug", message);
        }

        private static void InitialiseShutdownAction()
        {
            AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
        }

        private static void InitialiseTraceListeners()
        {
            var traceListener = new TextWriterTraceListener(_logFileStream);
            Trace.Listeners.Add(traceListener);
        }

        private static void InitialiseFileWriter()
        {
            if (File.Exists(logFile))
            {
                RolloverLogFile();
            }

            _logFileStream = new FileStream(logFile, FileMode.OpenOrCreate);
        }

        private static void RolloverLogFile()
        {
            var rollingFileName = logFile.Replace(".log",
                string.Format(".{0:yyyyMMddHHmmss}.log", File.GetLastWriteTime(logFile)));
            File.Move(logFile, rollingFileName);
        }

        private static void WriteEntry(string level, string message)
        {
            var module = GetCallingClass();
            Console.WriteLine(message);
            UdpBroadcast(module, level, message);
            Trace.WriteLine(string.Format(
                                  "{0}|{1}|{2}|{3}",
                                  DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                                  level,
                                  module,
                                  message));
            Trace.Flush();

           _logFileStream.Flush();
            
            if (new FileInfo(logFile).Length <= MaxFileSizeInMb*1000000) 
                return;

            RolloverLogFile();
            _logFileStream = new FileStream(logFile, FileMode.OpenOrCreate);
        }

        private static string GetCallingClass()
        {
            var stackTrace = new StackTrace();
            var stackFrames = stackTrace.GetFrames();
            if (stackFrames == null)
                return "Unknown";

            var parentStackFrame = stackFrames.First(x => x.GetMethod().DeclaringType != typeof (Logger));

            var declaringType = parentStackFrame.GetMethod().DeclaringType;
            return declaringType == null ? "Unknown" : declaringType.FullName;
        }

        private static void InitialiseUdpBroadcaster()
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _socketEndPoint = new IPEndPoint(UdpIp, UdpPort);
        }

        private static void UdpBroadcast(string module, string level, string message)
        {
            var xmlMessage = System.Security.SecurityElement.Escape(message);
            var log4jMessage = string.Format(log4JXmlMessageXml, module, (long)DateTime.Now.ToUniversalTime().Subtract(Epoch).TotalMilliseconds, level, Thread.CurrentThread.ManagedThreadId , xmlMessage,
                Environment.MachineName, Environment.UserDomainName, Environment.UserName, AppDomain.CurrentDomain.FriendlyName);
            var buffer = Encoding.ASCII.GetBytes(log4jMessage);
            _socket.SendTo(buffer, _socketEndPoint);
        }

        private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            _logFileStream.Close();
        }

        private static string log4JXmlMessageXml = 
        @"<log4j:event logger=""{0}"" timestamp=""{1}"" level=""{2}"" thread=""1"">
        <log4j:message>{3}</log4j:message> <log4j:properties> <log4j:data name=""log4jmachinename"" value=""{4}"" />
        <log4j:data name=""log4net:HostName"" value=""{4}"" /> <log4j:data name=""log4net:UserName"" value=""{5}"" />
        <log4j:data name=""log4japp"" value=""{6}"" /> </log4j:properties> </log4j:event>"; 
 
        ///Sample as provided in Log2Console source code 
        /// <log4j:event logger="Statyk7.Another.Name.DummyManager" timestamp="1184286222308" level="ERROR" thread="1"> 
        /// <log4j:message>This is an Message</log4j:message> 
        /// <log4j:properties> 
        /// <log4j:data name="log4jmachinename" value="remserver" /> 
        /// <log4j:data name="log4net:HostName" value="remserver" /> 
        /// <log4j:data name="log4net:UserName" value="REMSERVER\Statyk7" /> 
        /// <log4j:data name="log4japp" value="Test.exe" /> 
        /// </log4j:properties> 
        /// </log4j:event>
}

Usage

Now anywhere in you project just log using the Logger static object - no setup need, its all defaulted.
If you want you to configure it hack the class to be what you want (It's your projects code now)

Logger.Info("My Info message");

Credit

http://www.codeguru.com/csharp/.net/article.php/c19405/Tracing-in-NET-and-Implementing-Your-Own-Trace-Listeners.htm
https://log2console.codeplex.com

Update: 2016/01/28 - Updated code styling
                                - Update to check log level