In this article we go through adapting the connection string, we define the prototype of the event method, and go through adapting the DAL, and setting up the BLL.
Preface
When you use code from a CodeProject article, you may be among those that assume it will fit your needs precisely, and when it doesn't, you go running to the author. If you're lucky, the author is keeping an eye on the messages posted for the article, and if you're even luckier, he might even help you with the problem that YOU should have been able to fix on your own.
If you are someone like this, it is my opinion (and not a humble one) that you should cowboy up, and figure it out on your own. Google is a wonderful resource, and you can find the answer to almost ANY programming question you might come up with. This article is about self-sufficiency - how to muscle through using someone else' code without waving your panties for help.
NOTE: There is no source file associated with this article, as all the code is in the article proper, and in point of fact, the entire connection string class discussed herein is presented in its entirety at the end of the article.
Introduction
After writing the acode presented in this article - Generic DAL for ADO.Net - A Revisit [^], I started to "eat my own dog food" (use the code that I made available for others). The code in that article makes no assumptions about how you might want to use it, and most of the aspects of the code are implemented in such a way as to protect you from hurting yourself (or the code).
Adapting the Connection String
I needed a way to change one or more properties in the PWConnectionString
object that are used to construct the connection string. However, the properties in question are protected
, which means I needed to create a new class, and inherit the original PWConnectionString
class. That was easy enough, but the BLL
object didn't know how to get the new connection string if something changed.
Custom Events to the Rescue!
NOTE: All of the code presented in this section are put into the new connection string class.
My new connection string class needed to invoke a custom event that the BLL
could subscribe to. First, we need to define a class derived from EventArgs
.
public enum ConnStringChangedItem { Server, Database, UserID, Password, EncryptTraffic };
public class ConnStringArgs : EventArgs
{
public ConnStringChangedItem Changed { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
public ConnStringArgs(ConnStringChangedItem changed, string oldValue, string newValue)
{
this.Changed = changed;
this.NewValue = newValue;
}
}
I have to say that I don't actually need to know what the new or old values are, or even which property changed - right now. However, there may come a time that I *do* want to know those things, so I put them in the event arguments object so I don't have to do it later. This is what I call "defensive programming" - preparing for requirements that may reasonably surface in the future. It's a good practice to get into, and can cause you to be more introspective about your code, causing you to think more about how you approach a given programming task.
In the Class
First, we need to define the prototype of the event method. The method that handles the event needs to have the indicated return type, and accept the specified parameters.
public delegate void ConstringChangeHandler(object sender, ConnStringArgs e);
Next, we have to add the delegate method. This method creates the argument object, and then invokes the event delegate.
protected void Changed(ConnStringChangedItem changed, string oldValue, string newValue)
{
if (OnChanged != null)
{
ConnStringArgs args = new ConnStringArgs(changed, oldValue, newValue);
this.OnChanged(this, args);
}
}
Finally, I had to add public
methods (remember, the base class properties are protected, so we have to do it this way) that allow us to actually change the properties. After changing the property, the Changed
event is invoked. Since all of these methods are essentially the same, I'll just present one in this section.
public void ChangeDatabase (string name)
{
string oldValue = this.Database;
this.Database = name;
this.Changed(ConnStringChangedItem.Database, oldValue, this.Database);
}
Adapting the DAL
You should know by now, that you create a BLL object that inherits the DAL
, and that you implement your actual database accessor methods in it. Like the connection string class, the DAL is very narrowly focused and most of the properties and methods are protected. One aspect of the <code>DAL
is that it stores the connection string as a string, which makes responding to our new event impossible without some changes.
Setting Up the BLL
In order to be able to subscribe to a connection string event, we need to add a property for it.
public partial class BLL : DAL
{
public FactoryConnectionString EFConnString { get; set; }
Next, we have to provide constructors to satisfy the base DAL
class requirements. The first constructor is normal and simply uses the string provided as the connection string. If you use this constructor, you will not be able to utilize the new event feature we just added.
public BLL(string connstr) : base(connstr)
{
this.EFConnString = null;
}
Things get much more serious with the second constructor. This one accepts a connection string object. When we use this constructor, it calls the base matching constructor and sets the object's ConnectionString
property (which is a base64 encoded string), and sets its own EFConnString
property to the object. Once that's done, we add an event handler for the OnChanged event.
Notice I've implemented a destructor for the BLL. The reason is that if you add a custom event handler, you have to release it when the containing object goes null.
public BLL(FactoryConnectionString connStr) : base(connStr)
{
this.EFConnString = connStr;
this.EFConnString.OnChanged += this.EFConnString_OnChanged;
this.ConnectionString = this.EFConnString.ConnectionString;
}
~BLL()
{
if (this.EFConnString != null)
{
this.EFConnString.OnChanged -= this.EFConnString_OnChanged;
}
}
Finallty, we add the handler for the event. The BLL simply resets it's ConnectionString
property to the newly constructed EFConnString.ConnectionString
property value. Like I stated before, I don't need anything from the ConnStringArgs
parameter right now, so all we do is reset the base class' ConnectionString
property.
private void EFConnString_OnChanged(object sender, ConnStringArgs e)
{
this.ConnectionString = this.EFConnString.ConnectionString;
}
Using the Code
In my test app, I first create a connection "stringleton", and a bll singleton.
private static FactoryConnectionString EFConnString { get { return FactoryConnectionStringleton.Instance.EFConnString; } }
private static BLL BLL { get { return BLLSingleton.Instance.BLL; } }
static void Main(string[] args)
{
FactoryConnectionStringleton.Instance.Init("Test", "localhost", "ABCDEFG");
BLLSingleton.Instance.Init(EFConnString);
EFConnString.ChangeDatabase("HIJKLMNOP");
string test = BLL.ConnectionString.Base64Decode();
}
Points of Interest
When you're using other people's code, especially code that might get updated from time to time, it's important to remember that you can probably extend it and make it in your own image, without necessarily changing the original code, and by using the power of object-oriented programming. This means you have to know OOP basics, and to apply them in the language of choice, and exploit the features of the .Net framework. By the way, this same code should be perfectly compatible with .Net Core as well.
The connection string and BLL classes in their entirety:
public class FactoryConnectionString : PWConnectionString
{
public delegate void ConstringChangeHandler(object sender, ConnStringArgs e);
public event ConstringChangeHandler OnChanged;
public FactoryConnectionString(string name, string server, string database, bool encryptTraffic=false, string uid="", string pwd="")
: base(name, server, database, encryptTraffic, uid, pwd)
{
}
protected void Changed(ConnStringChangedItem changed, string oldValue, string newValue)
{
if (OnChanged != null)
{
ConnStringArgs args = new ConnStringArgs(changed, oldValue, newValue);
this.OnChanged(this, args);
}
}
public void ChangeDatabase (string name)
{
string oldValue = this.Database;
this.Database = name;
this.Changed(ConnStringChangedItem.Database, oldValue, this.Database);
}
public void ChangeServer (string name)
{
string oldValue = this.Server;
this.Server = name;
this.Changed(ConnStringChangedItem.Server, oldValue, this.Server);
}
public void ChangeUserID (string uid)
{
string oldValue = this.UserID;
this.UserID = uid;
this.Changed(ConnStringChangedItem.UserID, oldValue, this.UserID);
}
public void ChangePassword (string pwd)
{
string oldValue = this.Password;
this.Password = pwd;
this.Changed(ConnStringChangedItem.Password, oldValue, this.Password);
}
public void SetTrafficEncryption (bool encrypt)
{
string oldValue = this.EncryptTraffic.ToString();
this.EncryptTraffic = encrypt;
this.Changed(ConnStringChangedItem.EncryptTraffic, oldValue, this.EncryptTraffic.ToString());
}
}
public partial class BLL : DAL
{
public FactoryConnectionString EFConnString { get; set; }
public bool CanChangeConnString { get { return (this.EFConnString != null); } }
#region Constructors and Init
public BLL(string connstr) : base(connstr)
{
this.EFConnString = null;
}
~BLL()
{
if (this.EFConnString != null)
{
this.EFConnString.OnChanged -= this.EFConnString_OnChanged;
}
}
public BLL(FactoryConnectionString connStr) : base(connStr)
{
this.EFConnString = connStr;
this.EFConnString.OnChanged += this.EFConnString_OnChanged;
this.ConnectionString = this.EFConnString.ConnectionString;
}
private void EFConnString_OnChanged(object sender, ConnStringArgs e)
{
this.ConnectionString = this.EFConnString.ConnectionString;
}
#endregion Constructors and Init
#region business methods go here
#endregion business methods go here
}
History
- 2020.04.09 Initial release