20 December 2006
First Bulgarian .NET Book is here
17 November 2006
Windows Forms data binding and notification of changed properties
Yesterday a college of mine asked me a question: "How to refresh TextBox control in Windows forms application binded to a property of object, when the property change its value?". The solution is very simple, but according to me not very well documented (at least in books I read).
Solution: Provide event named PropertyNameChanged, where PropertyName is the name of property binded to the TextBox. During the binding process, the text box inspects binded object for event with the mentioned signature and binds to it. The sample code looks like this:
public class Customer
{
private string mName;
public string Name
{
get { return mName; }
set
{
mName = value;
if (NameChanged != null)
{
NameChanged(this, EventArgs.Empty);
}
}
}
public event EventHandler NameChanged;
}
Customer cust = new Customer();
textBox1.DataBindings.Add("Text", cust, "Name");
11 September 2006
Cool conference in Bulgaria
Google toolbar 4
The other features I like are SpellCheck and Translate. If you are inspired, install the toolbar from here for WinXP and IE. Enjoy!
04 September 2006
ADO.NET Entity Framework ContextObject and ASP.NET application
The idea is simple: Context object will be stored in ASP.NET session and after the request HTTPModule will free it. I'm using ASP.NET session instead of ASP.NET context to further extend this example to support session per application transaction strategy. If you are wondering what is "session per application transaction" take a look at NHibernate session management. Here is the code of the implementation:
public class ContextManager<T>where T: ObjectContext, new()
{
public static T ObjectContext
{
get
{
T resultContext;
if (HttpContext.Current.Items.Contains(
ContextConst.CONTEXT_OBJECT_SESSION_KEY))
{
resultContext =
(T)HttpContext.Current.Items[
ContextConst.CONTEXT_OBJECT_SESSION_KEY];
}
else
{
resultContext = new T();
HttpContext.Current.Items[ContextConst.CONTEXT_OBJECT_SESSION_KEY] =
resultContext;
}
return resultContext;
}
}
public static bool isActiveObjectContext
{
get
{
return HttpContext.Current.Items.Contains(
ContextConst.CONTEXT_OBJECT_SESSION_KEY);
}
}
}
public class ContextHttpModule : IHttpModule
{
#region IHttpModule Members
public void Init(HttpApplication context)
{
context.EndRequest += new EventHandler(OnEndRequest);
}
public void Dispose()
{
//Intentionaly not implemented
}
public void OnEndRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.Items.Contains(ContextConst.CONTEXT_OBJECT_SESSION_KEY))
{
ObjectContext contextObject =
(ObjectContext)HttpContext.Current.Items[
ContextConst.CONTEXT_OBJECT_SESSION_KEY];
contextObject.Dispose();
}
}
#endregion
}
internal class ContextConst
{
public const string CONTEXT_OBJECT_SESSION_KEY =
"ContextConst.CONTEXT_OBJECT_SESSION_KEY";
}
Now, lets look how the code above can be used. First lets make some corrections to web.config file:
<connectionStrings>
<add name="Duwamish7Model.Duwamish7"
connectionString="metadata=C:\Temp\ContextObject\Model;
mapping=C:\Temp\ContextObject\Model;
provider=System.Data.SqlClient;
provider connection string="Data Source=ns-server;
Initial Catalog=Duwamish7;Integrated Security=True""
providerName="System.Data.Mapping" />
</connectionStrings>
<httpModules>
<add name="ContextObjectManager"
type="ObjectContextManager.ContextHttpModule, ObjectContextManager"/>
</httpModules>
Duwamish7 db = ContextManager<Duwamish7>.ObjectContext;
Query<Authors> query = db.GetQuery<Authors>(
"SELECT VALUE a FROM Authors AS a WHERE a.PKId > 144");
Repeater1.DataSource = query;
Repeater1.DataBind();
P.S. The code above is not tested for concurrency problems.
03 September 2006
WCF and WS Eventing
24 August 2006
Finally it is true!
23 August 2006
Microsoft SQL Server and Ad-Hock queries optimization
SELECT ID, Name FROM Article WHERE Price > 100
SQL Server 2005 will replace constant literal values (100 in this example) by variables and this way query plan will be reuses for all queries similar to
SELECT ID, Name FROM Article WHERE Price > @p1
Not all of the ad-hock queries can be parameterized. If you are interested read this article.
Attribute based transaction management and NHibernate
public void Save(T entity)
{
bool manageTransaction = ! orm.isActiveTransaction;
if (manageTransaction)
{
orm.BeginTransaction(IsolationLevel.ReadCommitted);
}
try
{
orm.session.SaveOrUpdate(entity);
if (manageTransaction)
{
orm.CommitTransaction();
}
}catch(Exception e)
{
if (manageTransaction)
{
orm.RollbackTransaction();
}
throw e;
}
}
Almost 90% of the code is for transaction management. It will be nice if we can write something like:
[Transaction(IsolationLevel.ReadCommitted)]
public void Save(T entity)
{
mOrm.session.SaveOrUpdate(entity);
mOrm.session.Flush();
}
To achieve this, I will use Spring.NET to intercept method call of the DAO. Before method marked with TransactionAttribute is executed a transaction will be started and then committed after method is executed. Although .NET provides solution for intercepting method calls via ContextBoundObject, I will use Spring.NET because this way my DAO object must implement just one interface instead of inheriting from ContextBoundObject.
Lets begin with the implementation. First I will declare the attribute used to mark transaction sensitive methods:
[AttributeUsage(AttributeTargets.Method)]
public class TransactionAttribute : Attribute
{
private IsolationLevel mIsolationLevel;
public TransactionAttribute()
{
mIsolationLevel = IsolationLevel.ReadCommitted;
}
public TransactionAttribute(IsolationLevel aIsolation)
{
mIsolationLevel = aIsolation;
}
public IsolationLevel isolationLevel
{
get { return mIsolationLevel; }
set { mIsolationLevel = value; }
}
}
Now I will decrare the class responsible for starting and committing transactions. There are a lot of specific issues about method interception and Spring.NET so if you are not familiar with them read the documentation about Spring.NET. The class looks like:
public class TransactionAroundAdvice : IMethodInterceptor
{
OrmManager mOrm;
public TransactionAroundAdvice()
{
mOrm = OrmManagerFactory.GetInstance();
}
private TransactionAttribute GetTransactionAttribute(MethodInfo aMethod)
{
object[] attributes = aMethod.GetCustomAttributes(
typeof(TransactionAttribute), true);
if (attributes.Length != 1)
{
//TODO: Throw more meaningfull exception and message
throw new Exception("Transaction Attribute is missing");
}
return (TransactionAttribute)attributes[0];
}
public object Invoke(IMethodInvocation invocation)
{
bool manageTransaction = ! mOrm.isActiveTransaction;
if (manageTransaction)
{
TransactionAttribute transactionAttribute =
GetTransactionAttribute(invocation.Method);
mOrm.BeginTransaction(transactionAttribute.isolationLevel);
}
//TODO: Find way to compare isolation level from TransactionAttribute and
//current active transaction if manageTransaction == false
object returnValue;
try
{
returnValue = invocation.Proceed();
if (manageTransaction)
{
mOrm.CommitTransaction();
}
}catch(Exception e)
{
if (manageTransaction)
{
mOrm.RollbackTransaction();
}
if ((e is TargetInvocationException) && (e.InnerException != null))
{
//Because when AOP is enables, method is invoked via reflection
//TargetInvocationException is thrown. This exception is most likely
//to confuse application developer, so it is removed
throw e.InnerException;
}
else
{
throw e;
}
}
return returnValue;
}
}
When a method marked with TransactionAttribute is intercepted then Invoke method from TransactionAroundAdvice class is called. Now lets see how our DAO class looks like when transaction management code is removed:
public class AttributeEnabledDAO<T>: IDAO<T>
{
public AttributeEnabledDAO()
{
mOrm = OrmManagerFactory.GetInstance();
}
private OrmManager mOrm;
#region IDAO Members
public T Load(object id)
{
return (T) mOrm.session.Load(typeof(T), id);
}
[Transaction(IsolationLevel.ReadCommitted)]
public void Save(T entity)
{
mOrm.session.SaveOrUpdate(entity);
mOrm.session.Flush();
}
#endregion
}
Much more simple. Example of using AttributeEnabledDAO is given bellow:
ProxyFactory articleDAOFactory = new ProxyFactory(
new AttributeEnabledDAO<Article>());
articleDAOFactory.AddAdvisor(new DefaultPointcutAdvisor(
new AttributeMatchMethodPointcut(typeof(TransactionAttribute)),
new TransactionAroundAdvice()));
IDAO<Article> articleDao;
lock (synchObject)
{
articleDao = (IDAO<Article>) articleDAOFactory.GetProxy();
}
Article article = articleDao.Load(1);
article.Name = "Paracetamol";
article.SalePrice = rnd.NextDouble();
articleDao.Save(article);
16 August 2006
ADO.NET vNext CTP is out
NHibernate session management.
Presentation with features of Entity Data Model can be found here. If somehow Link* technologies looks messy for you, taka a look at this presentation.
06 August 2006
Querying Microsoft SQL Server trace files
05 August 2006
Interview with Anders Hejlsberg
Mocking static methods
Using TypeMock is pretty easy. In my previous post about NHibernate session management I'm using a static method to create appropriate session manager (take a look at GenericDAO constructor). The test of the constructor may be something like:
[TestFixture]
public class GenericDAOTest
{
[SetUp]
public void InitTest()
{
MockManager.Init();
//ormManagerMock = MockManager.Mock(typeof(PerRequestWebManager));
ormManagerFactory = MockManager.Mock(typeof(OrmManagerFactory));
}
//private Mock ormManagerMock;
private Mock ormManagerFactory;
private GenericDAO<Article> articleDAO;
[TearDown]
public void FinalizeTest()
{
MockManager.ClearAll();
}
[Test]
public void TestDAOConstructor()
{
ormManagerFactory.ExpectAndReturn("GetInstance",
new PerRequestWebManager());
articleDAO = new GenericDAO<Article>();
MockManager.Verify();
}
}
25 July 2006
NHibernate Session Management
NHibernate handles loading/saving objects to RDBMS via it's own implementation of session object. Until now, I'm familiar with 3 ways for handling NHibernate sessions:
- create session per each database operation - session is created before and destroyed after each operation with database. This is very ineffective way, because some features like lazy loading are unavailable. Also including several database operations in transaction is impossible.
- create session per each request - session is created before and destroyed after each HTTP request. This is my favorite strategy, because it is effective and very easy for implementation. I'll give example for possible implementation in this post.
- create session per each application transaction - session is created before and destroyed after each application transaction. Example for application transaction is adding invoice with invoice items.
So, lets begin with the implementation of session per request strategy. If you are reading this post you are most likely a developer and probably as most developers prefer to read source code instead of boring documentation. So I'll explain all of the classes in brief and will post full source code. First I'll declare a base abstract class for all of the session strategies.
public abstract class OrmManager
{
public OrmManager()
{
}
static NHibernate.Cfg.Configuration mConfiguration = null;
static volatile ISessionFactory mSessionFactory = null;
static object mFactorySyncObject = new object();
public abstract ISession session
{
get;
}
public IDbConnection sessionConnection
{
get { return session.Connection; }
}
public ISession NewSession()
{
ISessionFactory sessionFactory = GetSessionFactory();
return sessionFactory.OpenSession();
}
public abstract bool isActiveSession
{
get;
}
public abstract void closeSession();
private const string HBM_ASSEMBLY_NAME_SECTION = "HbmAssemblyName";
private ISessionFactory GetSessionFactory()
{
if (mSessionFactory == null)
{
lock (mFactorySyncObject)
{
if (mSessionFactory == null)
{
string hbmAssemblyName =
ConfigurationManager.AppSettings[HBM_ASSEMBLY_NAME_SECTION];
mConfiguration = new NHibernate.Cfg.Configuration();
mConfiguration.AddAssembly(hbmAssemblyName);
mSessionFactory = mConfiguration.BuildSessionFactory();
}
}
}
return mSessionFactory;
}
public void BeginTransaction(IsolationLevel isolation)
{
session.BeginTransaction(isolation);
}
public void CommitTransaction()
{
session.Transaction.Commit();
}
public void RollbackTransaction()
{
session.Transaction.Rollback();
}
public bool isActiveTransaction
{
get
{
return ((session.Transaction != null) &&
(!session.Transaction.WasCommitted) &&
(!session.Transaction.WasRolledBack));
}
}
}
Next, I'll implement the class responsible for session per request management. The following class implements IHttpModule so, we can handle end of the request and close any open sessions.
public class PerRequestWebManager : OrmManager, IHttpModule
{
public PerRequestWebManager()
{
}
private const string NHIBERNATE_SESSION_KEY = "NHIBERNATE_SESSION";
public override ISession session
{
get
{
ISession resultSession;
if (HttpContext.Current.Items.Contains(NHIBERNATE_SESSION_KEY))
{
resultSession =
(ISession)HttpContext.Current.Items[NHIBERNATE_SESSION_KEY];
}
else
{
resultSession = NewSession();
HttpContext.Current.Items[NHIBERNATE_SESSION_KEY] = resultSession;
}
return resultSession;
}
}
public override bool isActiveSession
{
get { return HttpContext.Current.Items.Contains(NHIBERNATE_SESSION_KEY); }
}
public override void closeSession()
{
if (isActiveSession == true)
{
if (isActiveTransaction == true)
{
RollbackTransaction();
}
session.Close();
HttpContext.Current.Items.Remove(NHIBERNATE_SESSION_KEY);
}
}
#region IHttpModule
public void Init(HttpApplication context)
{
context.EndRequest += new EventHandler(OnEndRequest);
}
public void Dispose()
{
//Intentionaly not implemented
}
public void OnEndRequest(Object sender, EventArgs e)
{
closeSession();
}
#endregion
}
Now I'll make one generic DAO class with the help of previos PerRequestWebManager.
public class GenericDAO<T>
{
public GenericDAO()
{
orm = OrmManagerFactory.GetInstance();
}
private OrmManager orm;
public T Load(object id)
{
return (T) orm.session.Load(typeof(T), id);
}
public void Save(T entity)
{
bool manageTransaction = ! orm.isActiveTransaction;
if (manageTransaction)
{
orm.BeginTransaction(IsolationLevel.ReadCommitted);
}
try
{
orm.session.SaveOrUpdate(entity);
if (manageTransaction)
{
orm.CommitTransaction();
}
}catch(Exception e)
{
if (manageTransaction)
{
orm.RollbackTransaction();
}
throw e;
}
}
}
public class OrmManagerFactory
{
private OrmManagerFactory()
{
}
private const string ORM_MANAGER_TYPE_SECTION = "OrmManager";
public static OrmManager GetInstance()
{
string ormType = ConfigurationManager.AppSettings[ORM_MANAGER_TYPE_SECTION];
if (ormType == typeof(PerRequestWebManager).Name)
{
return new PerRequestWebManager();
}
else
{
throw new Exception("Unknown ORM manager type");
}
}
}
And here's how all of the above can be used in ASP.NET application:
GenericDAO<Article> articleDao = new GenericDAO<Article>();
Article article = articleDao.Load(1);
article.Name = "Paracetamol";
article.SalePrice = rnd.NextDouble();
articleDao.Save(article);
If you are interested I can mail you full source of this post. Enjoy!
18 July 2006
Free Microsoft Office SharePoint Server 2007 Book
7 Development Projects for Microsoft Office SharePoint Server 2007 and Windows SharePoint Services 3.0. The book is free for download and is based on Beta 1 version of upcoming new version of SharePoint products.
17 July 2006
Formatting source code posted in blog
BTW, the source code in my previos post about GridView is formatted with this tool.
14 July 2006
GridView’s header and empty DataSource
The successor of DataGrid in ASP.NET 2.0 – GridView behaves unusually sometimes. For example if binded ObjectDataSource is empty, then GridView’s header is not rendered. This is not the required behavior in most of the cases. If you want to show the header of the GridView when binded data source is empty, there are several solutions, but I will describe the one I like most.
Solution in brief: Subclass GridView control, override CreateChildControls method and manualy create header if binded DataSource is empty. The implementation of CreateChildControls method may look like:
protected override int CreateChildControls(IEnumerable dataSource,
bool dataBinding)
{
int numRows = base.CreateChildControls(dataSource, dataBinding);
if (numRows == 0)
{
Table table = new Table();
table.ID = this.ID;
GridViewRow row = base.CreateRow(-1, -1,
DataControlRowType.Header, DataControlRowState.Normal);
DataControlField[] fields = new
DataControlField[this.Columns.Count];
this.Columns.CopyTo(fields, 0);
this.InitializeRow(row, fields);
table.Rows.Add(row);
this.Controls.Add(table);
}
return numRows;
}
The code above adds only a header to the grid. If you need you can add another empty row, where appropriate message may be shown.
Adding footer is a little bit tricky. GridView exposes a public property for managing the footer. So the footer added by you, must be accessible via this property. The solution to the problem is to override the virtual property FooterRow (To Microsoft ASP.NET 2 Team: Thank you for making this property virtual. Unfortunately some methods in ASP.NET 2 are not virtual and extending the framework sometimes is not so easy). According to the explanations above the implementation may look like:
private GridViewRow mFooterRow = null;
public override GridViewRow FooterRow
{
get
{
if (mFooterRow != null)
{
return mFooterRow;
}
else
{
return base.FooterRow;
}
}
}
protected override int CreateChildControls(IEnumerable dataSource,
bool dataBinding)
{
this.mFooterRow = null;
int numRows = base.CreateChildControls(dataSource, dataBinding);
if (numRows == 0)
{
Table table = new Table();
table.ID = this.ID;
GridViewRow row = base.CreateRow(-1, -1,
DataControlRowType.Header, DataControlRowState.Normal);
DataControlField[] fields = new
DataControlField[this.Columns.Count];
this.Columns.CopyTo(fields, 0);
this.InitializeRow(row, fields);
table.Rows.Add(row);
this.mFooterRow = base.CreateRow(-1, -1,
DataControlRowType.Footer, DataControlRowState.Normal);
fields = new DataControlField[this.Columns.Count];
this.Columns.CopyTo(fields, 0);
this.InitializeRow(mFooterRow, fields);
table.Rows.Add(mFooterRow);
this.Controls.Add(table);
}
return numRows;
}
During the creation of the component I remembered the old Delphi days. Creating new .NET control is almost as easy and funny as it was in Delphi.
WSS2 SP2 and .NET 2 Bug
A few weeks ago I was surprised by a very annoying bug in Windows SharePoint Services SP2 running under .NET Framework 2.0. When a page is customized with FrontPage (in this case the layout of the page is stored in SQL Server) and web part compiled with .NET 2.0 is imported to this page, the following exception is thrown:
Invalid postback or callback argument. Event validation is enabled using
<pages enableEventValidation="true"/> in configuration or
<%@ Page EnableEventValidation="true" %> in a page. For security
purposes, this feature verifies that arguments to postback or callback events
originate from the server control that originally rendered them. If the data is
valid and expected, use the ClientScriptManager.RegisterForEventValidation
method in order to register the postback or callback data for validation.
After some quick and unsuccessful attempts to resolve the exception, rapid search in google proved that this is really a bug.
Fortunately this bug is not present in SharePoint 2007 Beta 2 (I have tested it).