Tuesday, October 9, 2012

Strongly-typed id for MongoDb C# driver

MongoDb C# driver already has a lot of features however it still can be improved.
One particular idea is to use strongly typed id field instead of generic ObjectId type which will lead to checking correct id type at a compile-time and not in a run-time. For example, using id of this type you can ensure that you pass id of a product to a GetProductById method. Here is how it can be implemented (requires MongoDb c# driver) :
public class Id<T> : IEquatable<IId<T>>
{
public ObjectId Value { get;private set; }
public override string ToString()
{
return Value.ToString();
}
public Id(ObjectId value)
{
Value = value;
}
public static Id<T> GetNew()
{
return new Id<T>(ObjectId.GenerateNewId());
}
#region generated
public bool Equals(Id<T> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Value.Equals(Value);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(Id<T>)) return false;
return Equals((Id<T>)obj);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public static bool operator ==(Id<T> left, Id<T> right)
{
return Equals(left, right);
}
public static bool operator !=(Id<T> left, Id<T> right)
{
return !Equals(left, right);
}
public bool Equals(IId<T> other)
{
return Equals((object)other);
}
#endregion
}
view raw id.cs hosted with ❤ by GitHub
MongoDb requires id fields to be initialized when document is saved to database and in order to make developer's life easier and the code cleaner they provide mechanism for generation id when it does not exist. Here is its implementation for id:
public class IdGenerator<T> : IIdGenerator
{
public object GenerateId(object container, object document)
{
return Id<T>.GetNew();
}
public bool IsEmpty(object id)
{
var casted = id as Id<T>;
return casted == null || casted.Value == ObjectId.Empty;
}
}
view raw generator.cs hosted with ❤ by GitHub
After that id field can be declared in a following way. Note that after generator attribute has been set there is no need to set product id in a product constructor - it will be created when entity is saved to the database.
public class Product
{
[BsonId(IdGenerator = typeof(IdGenerator<Product>))]
public Id<Product> Id { get; private set; }
public string Name { get; set; }
public Decimal Price { get; set; }
}
view raw product.cs hosted with ❤ by GitHub

Tuesday, October 2, 2012

Writing multiline log messages into single line in NLog

I prefer to avoid writing single log message into several lines. Doing that allows me to analyze my logs using console tools like grep more efficiently.
While it is easy to meet this convention in custom log messages, user has to solve this problem for text which is put by several Nlog layout renderers, for example exception stacktrace renderer. To solve this problem I use replace layout wrapper. Take a look to a following nlog config:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<variable name="brief" value="${longdate} | ${level} | ${logger} | ${message} | ${exception:format=ToString}"/>
<variable name="verbose" value="${longdate} | ${processid} | ${processname} | ${threadid} | ${level} | ${logger} | ${message} | ${exception:format=ToString}"/>
<variable name="verbose_inline" value="${replace:inner=${verbose}:searchFor=\\r\\n|\\n:replaceWith=->:regex=true}"/>
<targets>
<target name="file" xsi:type="File" layout="${verbose_inline}" fileName="${basedir}/logs/mcserver_${shortdate}.log" />
<target name="console" xsi:type="ColoredConsole" layout="${brief}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="file" />
<logger name="*" minlevel="Trace" writeTo="console" />
</rules>
</nlog>
view raw gistfile1.xml hosted with ❤ by GitHub
In this example I use my custom verbose_inline layout that replaces all newlines to "->" string. After that I can easily sort out statistics for e.g. specific exception using grep command like:
grep -i 'System.NullReferenceException' *.log >nullref.txt
view raw gistfile1.txt hosted with ❤ by GitHub