Tuesday, 30 August 2011

C# Log4Net configuration for multiple processes with date rolling

This example uses MinimalLock locking Model to allow multiple processes to write to the same file

<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="Log\\MyApp.log"/>
  <appendToFile value="true"/>
  <rollingStyle value="Date"/>
  <datePattern value=".yyyyMMdd"/>
  <maxSizeRollBackups value="7"/>
  <staticLogFileName value="true"/>
  <threshold value="INFO"/>
  <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />                       
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%d [%t] %-5p %c.%M():%L - %m%n"/>
  </layout>
</appender>

Click here for more detailed post about Log4Net configuration in console apps

Friday, 26 August 2011

C# Send email with embedded image

string mailserver = "mymailserver";
            
string subject = "Email with picture";
string body = "<html><body><h1>Picture</h1><br><img src=\"cid:Pic1\"></body></html>";
string to = "to@email.com";
string from = "from@email.com";
string imagePath = @"c:\temp\myimage.png";
            
AlternateView view = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
LinkedResource pic = new LinkedResource(imagePath, MediaTypeNames.Image.Gif);
pic.ContentId = "Pic";
view.LinkedResources.Add(pic);
            
MailMessage m = new MailMessage();
m.AlternateViews.Add(view);
            
m.From = new MailAddress(from);
m.To.Add(new MailAddress(to));
m.Subject = subject;
SmtpClient client = new SmtpClient(mailserver);
client.Send(m);

Friday, 19 August 2011

SQL Delete in batches based on date

This is a sample how to delete data in batches to avoid large transaction log. This is a simpler version of this example when I used 2 tables.

DECLARE @MyTable TABLE 
( 
	Id int,
	DateUpdated DATETIME
)
INSERT INTO @MyTable
SELECT 1,DATEADD(d,-1,GETDATE())
UNION SELECT 2,DATEADD(d,-2,GETDATE())
UNION SELECT 3,DATEADD(d,1,GETDATE())
UNION SELECT 4,DATEADD(d,2,GETDATE())
UNION SELECT 5,DATEADD(d,-3,GETDATE())
UNION SELECT 6,DATEADD(d,-2,GETDATE())
UNION SELECT 7,DATEADD(d,-4,GETDATE())
DECLARE @date DATETIME
SET @date = GETDATE()
PRINT CONVERT(CHAR(19),GETDATE(),120) +' Deleting data in batches' 
WHILE (Exists(SELECT 1 FROM @MyTable WHERE DateUpdated < @date))
BEGIN TRY    
    BEGIN TRAN     
       PRINT CONVERT(CHAR(19),GETDATE(),120) +'   Deleting 2..'   
       DELETE TOP(2) @MyTable FROM @MyTable WHERE DateUpdated < @date
    COMMIT TRAN 
END TRY
BEGIN CATCH 
    ROLLBACK
END CATCH
--SELECT * FROM @MyTable

Friday, 12 August 2011

Retry class implemented in functional C#

public class Retry
{
  public static ILog _log = LogManager.GetLogger(MethodInfo.GetCurrentMethod().DeclaringType);
  int _retryAttempts = 3;
  int _retryWaitSeconds = 1;
  public Retry() { }
  public Retry(int retryAttempts, int retryWaitSeconds)
  {
    _retryAttempts = retryAttempts;
    _retryWaitSeconds = retryWaitSeconds;
  }
  public void Execute(Action action)
  {
    int attempt = 0;
    while (attempt++ < _retryAttempts)
    {
      try
      {
        if (_log.IsDebugEnabled)
        {
          _log.DebugFormat("Retry attempt {0}/{1}", attempt, _retryAttempts);
        }
        action();
        return;
      }
      catch (Exception e)
      {
        if (attempt >= _retryAttempts)
        {
          _log.ErrorFormat("Retry failed with {0} attempts", _retryAttempts);
          throw e;
        }
        int wait = _retryWaitSeconds * attempt;
        _log.Warn(string.Format("Retry attempt {0}/{1} failed, waiting {2}s", attempt, _retryAttempts, wait), e);
        Thread.Sleep(1000 * wait);
      }
    }
  }
  public TResult Execute<TResult>(Func<TResult> func)
  {
    int attempt = 0;
    while (attempt++ < _retryAttempts)
    {
      try
      {
        if (_log.IsDebugEnabled)
        {
          _log.DebugFormat("Retry attempt {0}/{1}", attempt, _retryAttempts);
        }
        return func();
      }
      catch (Exception e)
      {
        if (attempt >= _retryAttempts)
        {
          _log.ErrorFormat("Retry failed with {0} attempts", _retryAttempts);
          throw e;
        }
        int wait = _retryWaitSeconds * attempt;
        _log.Warn(string.Format("Retry attempt {0}/{1} failed, waiting {2}s", attempt, _retryAttempts, wait), e);
        Thread.Sleep(1000 * wait);
      }
    }
    throw new Exception("Retry failed");
  }
}
[TestFixture]
public class TestRetry
{
  [Test]
  public void Test()
  {
    new Retry(3, 1).Execute(SuccessAction);
    try
    {
      new Retry(3, 1).Execute(FailureAction);
    }catch(Exception ex)
    {
      Console.WriteLine("Failed");
    }
    Console.WriteLine(new Retry(3, 1).Execute(() => ComplexAction("1", "2")));
    Console.WriteLine(new Retry(3, 1).Execute(() => ObjectAction("John")).ToString());
    Func<Person> func = () =>
    {
      return new Person() { Name = "John" };
    };
    Console.WriteLine(new Retry(3, 1).Execute(func));
  }
  private void FailureAction()
  {
    Console.WriteLine("{0}: Failed", DateTime.Now);
    throw new Exception("Failed Exception");
  }
  private void SuccessAction()
  {
    Console.WriteLine("{0}: OK", DateTime.Now);
  }
  private string ComplexAction(string a, string b)
  {
    return String.Format("{0} {1}", a, b);
  }
  private Person ObjectAction(string name)
  {
    return new Person() { Name = name };
  }
}
class Person
{
  public string Name { get; set; }
  public override string ToString()
  {
    return "My name is : " + Name;
  }
}

Wednesday, 3 August 2011

Visual Studio Web Server not updating/caching pages

I came across a problem today when visual studio development web server was not updating when compiling new code. After deleting bin folder from my Web project I got this error:

image

The problem was with build output location. For web projects the Output path in web project properties should be just bin not bin\Debug or bin\x86\Debug because web server root folder is your Web project folder.

C# MVC3 Log4Net configuration

Add log4net section to top level Web.config
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>
  [...]
  <log4net>
   <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
      <param name="File" value="Log\\MyProject.log" />
      <param name="AppendToFile" value="true" />
      <param name="RollingStyle" value="Size" />
      <param name="MaxSizeRollBackups" value="10" />
      <param name="MaximumFileSize" value="500KB" />
      <param name="StaticLogFileName" value="true" />
      <param name="Threshold" value="INFO"/>
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="%d [%t] %-5p %c.%M():%L - %m%n" />
      </layout>
    </appender>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
      <param name="Threshold" value="DEBUG"/>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%d [%t] %-5p %c.%M():%L - %m%n" />
      </layout>
    </appender>
    <logger name="MyCompany">
      <appender-ref ref="RollingFileAppender" />      
      <appender-ref ref="ConsoleAppender" /> 
    </logger>
  </log4net>
</configuration>

Initialize log4net from Application_Start() method in Global.asax.cs

protected void Application_Start()
{
  [...]
  log4net.Config.XmlConfigurator.Configure();
}

Click here for more detailed post about Log4Net configuration in console apps