A different approach for saving programmer changes while using Code Generator
Before I’ll talk about a different solution, let’s talk about the default solution so we could compare them.
Demonstrate the (default)missionary approach – Inheritance:
Let’s view a very simple piece of code to make things a bit more tangible:
UsersDal.generated.cs file:
This file is auto-generated based on Users table from our database:
// This file is auto-generated.
// Any changes to this file can be lost in the next code generation.
// If you need to change something – check UsersDal.cs
public abstract class UsersDalBase
{
public virtual void Add(User u)
{
Console.WriteLine(“Doing \”base\” behavior for UsersDalBase.Add(User u)”);
}
}
You can imagine that in reality, UsersDalBase.Add method will actually open a connection against the database and insert the received user object. As you can tell, this file should be changed only by the code generator (if you change the table structure or relations for example).
Let’s look at our stub class that inherit from the “Base” class.
UsersDal.cs file:
// This file is safe for edit !
public class UsersDal : UsersDalBase
{
}
This file is also generated but only as an empty stub class which can be used if we(the programmers) need to change the default behavior in UsersDalBase. Any upper layer(Business Layer, WS, whatever) in our application will use only UsersDal so any changes to UsersDal or UsersDalBase will be reflected automatically.
Now, let’s say that the generated code isn’t good enough. I want to check some conditions on the user object before actually adding it to our database. All I have to do is simply override the Add method in our UsersDal class.
public class UsersDal : UsersDalBase
{
public override void Add(User u)
{
if (Validator.IsValid(u))
{
base.Add(u);
}
else
{
throw new ArgumentException(“The given object is invalid.”);
}
}
}
Now, if I want to re-generate my code due to some changes in my ERD, all I have to do is to replace the UsersDalBase.generated.cs file and my custom changes remains untouched.
The only problem with inheritance is it’s limitation:
(1) You can inherit from one class only and by using inheritance I can’t inherit from GenericDal (unless I’ll add another layer of inheritance, not ideal).
(2) I need to give “high” access modifier(public) to my UsersDalBase. We use UsersDalBase just to allow custom changes. In a perfect world UsersDalBase should be internal (Data Access Layer project).
Let’s look at a different solution – events based separation:
We want to keep our original ability to make changes in our code without losing it on the next code-generation. First, let’s look at a very raw UsersEventArgs I’ll use later on:
public class UserEventArgs : EventArgs
{
private User m_user;
private bool m_cancel;
public UserEventArgs(User u)
{
m_user = u;
}
public User User
{
get { return m_user; }
set { m_user = value; }
}
public bool Cancel
{
get { return m_cancel; }
set { m_cancel = value; }
}
}
Notice the “Cancel” property, we’ll use it later on.
UsersDal.generated.cs (version 2)
public partial class UsersDal
{
protected event EventHandler<UserEventArgs> Adding;
protected event EventHandler<UserEventArgs> Added;
public void Add(User u)
{
UserEventArgs ea = new UserEventArgs(u);
RaiseAdding(ea);
if (!ea.Cancel)
{
// default logic
AddUser(u);
RaiseAdded(ea);
}
}
protected virtual void AddUser(User u)
{
// generated code
Console.WriteLine(“Doing \”base\” behavior for UsersDal.Add”);
}
protected void RaiseAdding(UserEventArgs e)
{
EventHandler<UserEventArgs> handler = Adding;
if (handler != null)
handler(this, e);
}
protected void RaiseAdded(UserEventArgs e)
{
EventHandler<UserEventArgs> handler = Added;
if (handler != null)
handler(this, e);
}
}
(1) As you can see, I’m raising an event just before adding the user and another event just after adding the user.
(2) We are using partial class (UsersDal).
UsersDal.cs (version 2):
public partial class UsersDal
{
public UsersDal()
{
// Register to (self) events:
this.Adding += new EventHandler<UserEventArgs>(UsersDal_OnAdding);
this.Added += new EventHandler<UserEventArgs>(UsersDal_Added);
}
protected void UsersDal_OnAdding(object sender, UserEventArgs e)
{
if (!Validator.IsValid(e.User))
{
e.Cancel = true;
// I can also throw a new exception, that will work as well.
}
// If e.Cancel remains false (default) the basic (generated)behavior will be executed.
}
void UsersDal_Added(object sender, UserEventArgs e)
{
// Console.WriteLine(“Doing custom UsersDal.Add – on added”);
}
}
Calling e.Cancel = true will actually stop the flow in the generated file. You can obviously add more code – according to your needs.
This separation will able you to do just about everything you could via inheritance and then some:
(1) The ability to add another partial class in order to separate class by “topics”.
(2) The ability to add more listeners(event handlers).
(3) The ability to inherit from other classes, if you need to.
(4) No need for extra class in our namespace which means less confusion to the programmer – “Should I use UsersDalBase ? Oh.. wait… I remember something about it… Ya! I need to use UsersDal…”.
Hope it helps.