Thursday, 19 June 2008

Write to different files with Log4Net

Logging in dot.Net using Log4Net is rather easy.
We often want to write all standard infos, errors and warnings to one rolling file.
To configure this you do the following
- configure one or more appenders
- add a root logger
- configure the root logger to use your appenders

Now, if you want to write in certain cases to another file, all you need to do is to create an extra named logger and attach the appender of your choice.

Here is the config
<log4net>
  <!-- create all appenders you need here -->
  <appender name="Console" type="log4net.Appender.ConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
    <!-- Pattern to output the caller's file name and line number -->
    <conversionPattern value="%5level [%thread] (%file:%line) - %message%newline"/>
  </layout>
</appender>

<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file value="MyProject.log"/>
<appendToFile value="true"/>
<rollingStyle value="composite"/>
<datePattern value="yyyyMMdd"/>
<maximumFileSize value="5MB"/>
<maxSizeRollBackups value="20"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level [%thread] %logger - %message%newline"/>
</layout>
</appender>

<appender name="SpecialAuditFile" type="log4net.Appender.RollingFileAppender">
   <file value="SpecialAudit.log"/>
   <appendToFile value="true"/>
   <rollingStyle value="composite"/>
   <datePattern value="yyyyMMdd"/>
   <maximumFileSize value="5MB"/>
   <maxSizeRollBackups value="20"/>
   <layout type="log4net.Layout.PatternLayout">
     <conversionPattern value="%date %-5level [%thread] %logger - %message%newline"/>
   </layout>
</appender>

<!-- These are your loggers: one root which is always there and one named logger -->

<root>
<level value="ERROR"/>
<appender-ref ref="Console"/>
<appender-ref ref="RollingFile"/>
</root>

<logger name="SpecialAudit" additivity="false">
  <level value="ALL" />
  <appender-ref ref= "SpecialAuditFile" />
</logger>

</log4net>

Don't forget to add the section in the configSections region
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />

And here is how you get the one and the other:
// logger for class hierarchy
private static readonly ILog logger = LogManager.GetLogger(typeof(MyClass));
// named logger
private static readonly ILog logAudit = LogManager.GetLogger("SpecialAudit");


In some cases (like Test in NUnit) an explicite call to the conifuration manager is a good idea.

XmlConfigurator.Configure();
...

And again for some test scenarios you should indicate the config filename.
You can do this with an assembly attribute in the AssemblyInfo.cs

...
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "ues.myproject.core.test.dll.config", Watch = true)]
...


The basic intro from Apache is here
Wednesday, 28 May 2008

How to disable all foreign key constraints on all tables in SQL Server?

The other day I wanted to import some table data from Excel using the import data facility. But, as it goes, SQL Server insisted to import some tables having FKs before their corresponding PK tables. So we have to disable/enable FK constraints on some or all tables.
Here is a very usefull script from decipherinfosys
I tried this out on SQL server 2000. Its working fine.

/************** DISABLE ALL TABLE CONSTRAINTS *********************************

This script will disable all constraints on all tables within the database
that it is run in.

******************************************************************************/

SET NOCOUNT ON
SET ROWCOUNT 0

DECLARE @Count int
DECLARE @String nvarchar (1000)
DECLARE @ConstraintName varchar(128)
DECLARE @TableName varchar(128)

--Find all constraints and their respective tables from the sysobjects table and place into a temp table.
--Primary Key and Unique Constraints via Unique Indexes are not disabled through this command
--You should use the ALTER INDEX...DISABLE command in SQL Server 2005
SELECT
name AS constraintname,
object_name(parent_obj) AS tablename
INTO #Const_Table
FROM sysobjects s
where xtype in ('F')

SELECT @Count = Count(*) FROM #Const_Table

--Setting the rowcount to one allows for one row from the temp table to be picked off at a time.
--Used as an alternative to a cursor.
SET ROWCOUNT 1

--Loop until all rows in temp table have been processed.
WHILE @Count > 0
BEGIN
--The rowcount of one ensures that only one tablename and constraint name is picked.
SELECT @TableName = TableName, @ConstraintName = ConstraintName
FROM #Const_Table

--Build execution string to disable constraint.
SET @String = 'ALTER TABLE ['+ @tablename + '] NOCHECK CONSTRAINT [' + @constraintname +']'

--Execute the SQL
exec sp_executesql @string

--Remove this row from the temp table, since it has now been processed.
DELETE FROM #Const_Table WHERE ConstraintName = @ConstraintName and TableName = @TableName

SET @Count = @Count - 1
END -- Loop

set rowcount 0


After having re-enabled the constrains you can check constraints using the following command:

DBCC CHECKCONSTRAINTS

And look at your constraints using this command:

SELECT (CASE
WHEN OBJECTPROPERTY(CONSTID, 'CNSTISDISABLED') = 0 THEN 'ENABLED'
ELSE 'DISABLED'
END) AS STATUS,
OBJECT_NAME(CONSTID) AS CONSTRAINT_NAME,
OBJECT_NAME(FKEYID) AS TABLE_NAME,
COL_NAME(FKEYID, FKEY) AS COLUMN_NAME,
OBJECT_NAME(RKEYID) AS REFERENCED_TABLE_NAME,
COL_NAME(RKEYID, RKEY) AS REFERENCED_COLUMN_NAME
FROM SYSFOREIGNKEYS
ORDER BY TABLE_NAME, CONSTRAINT_NAME,REFERENCED_TABLE_NAME, KEYNO
Thursday, 22 May 2008

IEnumerable, IEnumerators, IEnumerable<string> and yield

If you want to use custom data collection in the foreach loop, you have to make sure that the collection imprements a GetEnumerator() method that returns an IEnumerator interface.
In order to indicate that your list implements the GetEnumerator() method you can implement the IEnumerable interface as follows:


public class MyEnumerableList : IEnumberable
{
public IEnumerator GetEnumerator(){...}

}


Here are the framework definitions for .NET Framework 1

public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current{get;}
bool MoveNext();
void Reset();
}


Here is a custom class that implements an iterator in .Net 1.

public class MyNumbers : IEnumerable
{
string[] _numberNames = { "Eins", "Zwei", "Drei", "Vier" };

IEnumerator IEnumerable.GetEnumerator()
{
return new MyEnumerator(this);
}

IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)this).GetEnumerator();
}

//Inner class for the iteration
class MyEnumerator : IEnumerator
{
// inner collection
MyNumbers _myNumbers;
// keep the index in mind
int _current;
// constructor
public MyEnumerator(MyNumbers collection)
{
_myNumbers = collection;
_current = -1;
}
void IEnumerator.Reset()
{
_current = -1;
}
bool IEnumerator.MoveNext()
{
_current++;
return (_current < _myNumbers._numberNames.Length);
}
string IEnumerator.Current
{
get
{
if (_current == -1) throw new InvalidOperationException();
return _myNumbers._numberNames[_current];
}
}
object IEnumerator.Current
{
get
{
return ((IEnumerator)this).Current;
}
}
public void Dispose() { }
}
}


In the .NET Framework 2.0 they added a generic interface that inherits from the older one.

public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<T> : IEnumerator,IDisposable
{
T Current{get;}
}


Add now thanks to the new yield statement the compiler generates the inner iterator class itself in order to keep the state.

public class MyNumbers2 : IEnumerable<string>
{
string[] _numberNames = { "Eins", "Zwei", "Drei", "Vier" };
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
for (int i = 0; i < _numberNames.Length; i++)
yield return _numberNames[i];
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<string>)this).GetEnumerator();
}
}

Wednesday, 21 May 2008

Just created this blog

I've just created this new blog about things I found on the web or made up and like to put somewhere if not in mind.

This will be mainly about IT, programming, philosophy, language learning etc.

"Donkey bridge" is not really English - the correct word would be "Mnemonic Device". But I prefer the direct and literal translation "DONKEY BRIDGE" of the expression we use in German: "Eselsbrücke".