Active Record Pattern
I want to talk about applying the Active Record template for C # in practice. Such a class implements the extraction and writing of the structure to the database. Business logic is carried out to the following levels of abstraction, where one can work with such an object as with a regular structure.
The central case, which I will consider as an example, is working with the Country directory from a database, which is often read, but very rarely changes.
Using an active record object in business logic code looks like this:
Country does not have a public constructor, and getting objects is possible only through accessing the Dictionary <string, Country> All method .
Now in more detail about how this class is built from the inside.
Due to the privacy of the constructor, we can hope for the correct use of the constructor. And only inside the class.
For example, like this:
sqlSelect is a private constant for reading all records with the required field order for the constructor.
DBWrapper is a self-written class that encapsulates working with a database. Thanks to him, at this level we only have to work with interfaces, without specifying a specific implementation.
Add - adding a new entry to the general registry is hidden for the brevity of the code in the article.
Nothing complicated here either:
As a result, we have a delayed loading of the directory on the first call.
The private variable _all uses only in this piece of code. Unfortunately, C # does not allow restricting its use to less than the entire class. There remains the danger of its application, for example, in public methods. What will become a real problem when working in multiple threads.
This is the first question I want to discuss: how to more restrict the visibility of the _all variable?
This deferred loading method is not yet suitable for multithreading, so I added the LoadStatus class .
The name for the status is needed to identify it in the status list of all directories. But more on that later.
A lot of pasta, but now we have multithreading and a health report of our directory, as a bonus from architecture.
LoadStatus hides the synchronization and collection of health data in a directory.
In addition, by resetting LoadStatus , it becomes possible to reload the directory on the fly.
It was for the sake of this opportunity that I refused readonly for _all .
The solution turned out to be so convenient and elegant that I use it in dozens of directories in all projects. And there is a great desire to turn this code into a generic class .
However, the C # syntax does not allow this.
What do you think of this decision?
What can be ways to turn this solution into generic ?
The central case, which I will consider as an example, is working with the Country directory from a database, which is often read, but very rarely changes.
Using an active record object in business logic code looks like this:
Country russia = Country.All[“Russia”];
Country does not have a public constructor, and getting objects is possible only through accessing the Dictionary <string, Country> All method .
Now in more detail about how this class is built from the inside.
Record constructor
publicreadonlystring name;
privateCountry(IDataRecord record)
{
name = record.GetString(0);
}
Due to the privacy of the constructor, we can hope for the correct use of the constructor. And only inside the class.
For example, like this:
privatestatic Dictionary<string, Country> _all = null;
privatestatic Dictionary<string, Country> LoadDictionary()
{
_all = new Dictionary<string, Country>();
IDataReader reader = DBWrapper.GetReader(sqlSelect);
try
{
while (reader.Read())
{
Country item = new Country(reader);
_all.Add(item.name, item);
}
}
finally { reader.Close(); }
return _all;
}
sqlSelect is a private constant for reading all records with the required field order for the constructor.
DBWrapper is a self-written class that encapsulates working with a database. Thanks to him, at this level we only have to work with interfaces, without specifying a specific implementation.
Add - adding a new entry to the general registry is hidden for the brevity of the code in the article.
All Dictionary
Nothing complicated here either:
publicstatic Dictionary<string, Country> All
{ get {
return _all ?? LoadDictionary();
}}
As a result, we have a delayed loading of the directory on the first call.
The private variable _all uses only in this piece of code. Unfortunately, C # does not allow restricting its use to less than the entire class. There remains the danger of its application, for example, in public methods. What will become a real problem when working in multiple threads.
This is the first question I want to discuss: how to more restrict the visibility of the _all variable?
Multithreading synchronization
This deferred loading method is not yet suitable for multithreading, so I added the LoadStatus class .
private static readonly LoadStatus statusCountryList = new LoadStatus(“country”);
The name for the status is needed to identify it in the status list of all directories. But more on that later.
privatestatic Dictionary<string, Country> _all = null;
publicstatic Dictionary<string, Country> All
{ get {
if ( !statusCountryList.IsCompleted ) {
lock (statusCountryList) {
if ( !statusCountryList.IsCompleted ) {
statusCountryList.Start();
_all = new Dictionary<string, Country>();
IDataReader reader= DBWrapper.GetReader(sqlSelect);
try
{
while (reader.Read()) Add(new Country(reader))
statusCountryList.Finish(_all.Count);
}
catch Exception ex { statusCountryList.Error(ex); }
finally { reader.Close(); }
}}}
return _all;
}}
A lot of pasta, but now we have multithreading and a health report of our directory, as a bonus from architecture.
LoadStatus hides the synchronization and collection of health data in a directory.
In addition, by resetting LoadStatus , it becomes possible to reload the directory on the fly.
It was for the sake of this opportunity that I refused readonly for _all .
Class generic
The solution turned out to be so convenient and elegant that I use it in dozens of directories in all projects. And there is a great desire to turn this code into a generic class .
However, the C # syntax does not allow this.
What do you think of this decision?
What can be ways to turn this solution into generic ?