After having implemented a data layer in the Data project, it was time to make a real data implementation. A
Sql Server 2000 implementation was the default data source, located in the
Data.SqlServer project.
Enterprise Library was used to provide the data access to
Sql Server. This contained a
Data Access Application Block, which allows configuring the connection string through the
Enterprise Library Configuration tool.
A reference to
Microsoft.Practices.EnterpriseLibrary.Data was needed, together with the
Configuration and
Common assemblies of
Enterprise Library.
Through the
Enterprise Library Configuration tool, an existing App.config was loaded, where the
Data Access Application Block was added. The
database and
server values had to be configured to the actual server being used, together with the database containing the data. Additional connection string properties could be added as well, for example, the
Integrated Security property, which is set to
True.

After saving this file, it was possible to create a data implementation for each
Accessor interface previously defined in the
Data project, as for example this code:
using System;
using System.Data;
using System.Collections;
using MediaService.Logging;
using MediaService.Objects;
using MediaService.Data.Accessors;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.Logging;
namespace MediaService.Data.SqlServer {
public class SongDataAccessor: ISongDataAccessor {
} /* SongDataAccessor */
} /* MediaService.Data.SqlServer */
Thanks to the
Enterprise Library Data Access Application Block, the
Sql Server implementation used best practices from the
Microsoft Patterns & Practices group, which followed
Microsoft guidelines and were optimized for performance.
To get an array of objects from the database, a new
Database object had to be created, after which a stored procedure was wrapped, called and read from to get for example
Song objects. This was done with the following code:
public Song[] GetSongs() {
Database db = DatabaseFactory.CreateDatabase("MediaServiceSqlServer");
DBCommandWrapper dbCommandWrapper =
db.GetStoredProcCommandWrapper("GetSongs");
Logger.Write("Retrieving songs.", Category.SqlServer,
Priority.Lowest, 1, Severity.Information);
ArrayList songs = new ArrayList();
using (IDataReader dataReader = db.ExecuteReader(dbCommandWrapper)) {
while (dataReader.Read()) {
songs.Add(new Song(dataReader.GetInt32(0), dataReader.GetString(1),
dataReader.GetString(2), dataReader.GetString(3),
dataReader.GetString(4), dataReader.GetString(5),
dataReader.GetString(6), dataReader.GetInt32(7),
dataReader.GetInt32(8), dataReader.GetInt32(9)));
}
}
Logger.Write(String.Format("Retrieved {0} {1}.", songs.Count,
(songs.Count == 1) ? "song" : "songs"),
Category.SqlServer, Priority.Lowest, 1, Severity.Information);
return (Song[])songs.ToArray(typeof(Song));
} /* GetSongs */
Updating an item by using a stored procedure which uses parameters, was done by using the following code:
public void UpdateSongPlayCount(Int32 songId) {
Database db = DatabaseFactory.CreateDatabase("MediaServiceSqlServer");
DBCommandWrapper dbCommandWrapper =
db.GetStoredProcCommandWrapper("UpdateSongPlayCount");
dbCommandWrapper.AddInParameter("@songId", DbType.Int32, songId);
Logger.Write(String.Format("Updating play count for song: {0}.", songId),
Category.SqlServer, Priority.Lowest, 1, Severity.Information);
try {
db.ExecuteNonQuery(dbCommandWrapper);
} catch (Exception ex) {
Logger.Write(String.Format("Failed to update play count for song: {0}.
Error: {1}", songId, ex.ToString()),
Category.SqlServer, Priority.Highest, 1, Severity.Error);
}
} /* UpdateSongPlayCount */
Using stored procedures made it possible to have another layer of abstraction. This made it easy changing an existing stored procedure to keep track of statistics, without having to change any code of the implementation. At the same time, using stored procedures also protected against
Sql Injection attacks. After all
Accessors were implemented, it was possible to use this implementation by deploying the
SqlServer dll and selecting it as data source.
Any application using data benefits from having a separate data layer. This enables the administrator to select which data source to use. It also makes your application have an advantage, making it easier to sell.
Besides from the advantages for the end-users, it’s also best practices to separate the data layer from your presentation and business logic layer.
To provide the data layer to the application a Data project was added. The layers above the data layer never accessed the real data implementations, but worked with objects which implemented certain data interfaces. This way, it was possible to define all possible data related methods in an interface and afterwards implement them in a real implementation.
A logical grouping was applied when creating the interfaces, starting from a generic
IDataAccessor from which every other interface inherited from.
using System;
namespace MediaService.Data.Accessors {
public interface IDataAccessor {
} /* IDataAccessor */
} /* MediaService.Data.Accessors */
One of the logical sections was for example everything related to
Song objects:
using System;
using MediaService.Objects;
namespace MediaService.Data.Accessors {
public interface ISongDataAccessor: IDataAccessor {
Song[] GetSongs();
Song[] GetQueue();
Song[] GetMostPlayed(Int32 numberOfSongs);
Song[] GetMostPopular(Int32 numberOfSongs);
} /* ISongDataAccessor */
} /* MediaService.Data.Accessors */
Since the other projects did not have a reference to the real data implementations, but only to the
Data project, this project had to take care of loading the correct implementation. Loading the correct class in the real implementation is done by using factories. For every
Accessor interface a factory exists, returning an instance of the data implementation, using the following code:
using System;
using MediaService.Data.Accessors;
namespace MediaService.Data.Factory {
internal class SongFactory: Factory {
internal static ISongDataAccessor Create() {
return Factory.Create(Accessor.Song) as ISongDataAccessor;
} /* Create */
} /* SongFactory */
} /* MediaService.Data */
In the
Data project, there was one
Factory class, responsible for loading the correct assembly containing the data implementation and instantiating the correct
Accessor class. This was done by using
Reflection together with
Configuration to retrieve the location. The location consisted out of the class name and the assembly name, separated by a comma, as for example the location for the
SongDataAccessor:
<SongDataAccessor>
MediaService.Data.SqlServer.SongDataAccessor,MediaService.Data.SqlServer
</SongDataAccessor>
This location data was retrieved by configuration, after which it was separated into assembly and class parts and loaded with
Reflection with the following code:
using System;
using System.Reflection;
using MediaService.Configuration;
using MediaService.Data.Accessors;
using Microsoft.Practices.EnterpriseLibrary.Configuration;
namespace MediaService.Data.Factory {
internal enum Accessor {
Song
} /* Accessor */
internal class Factory {
internal static IDataAccessor Create(Accessor accessorType) {
DatabaseData configData = LoadConfiguration();
if (configData == null) {
throw new ApplicationException("Could not load configuration.");
}
String blockToLoad = String.Empty;
switch (accessorType) {
case Accessor.Song: blockToLoad = configData.SongDataAccessor; break;
}
if (blockToLoad == String.Empty) {
throw new ApplicationException(String.Format(
"Type entry not found for {0}.", accessorType.ToString()));
}
Int32 index = blockToLoad.IndexOf(",");
string typeToLoad = blockToLoad.Substring(0,index);
string assemblyToLoad = blockToLoad.Substring(typeToLoad.Length + 1,
blockToLoad.Length - typeToLoad.Length - 1);
return (IDataAccessor)Assembly.Load(
assemblyToLoad).CreateInstance(typeToLoad);
} /* Create */
private static DatabaseData LoadConfiguration() {
ConfigurationManager.ClearSingletonSectionCache("databaseConfiguration");
return ConfigurationManager.GetConfiguration(
"databaseConfiguration") as DatabaseData;
} /* LoadConfiguration */
} /* Factory */
} /* MediaService.Data.Factory */
All of the
Factories were marked internal because they are just meant for internal workings of the data layer, while all
Accessors remain public because they had to be accessible to implement in the real data implementation.
Besides the
Accessor interfaces, the
Data project also exposed one public class, named Dalc. This class contained static properties for each logical data section, returning an instantiated
Accessor from the configured data source.
using System;
using MediaService.Data.Accessors;
using MediaService.Data.Factory;
namespace MediaService.Data {
public class Dalc {
public static ISongDataAccessor Song {
get { return SongFactory.Create(); }
} /* Song */
} /* Dalc */
} /* MediaService.Data */
After this, it was possible to access data by adding a reference to the
Data project, adding a real data implementation assembly to the deployed location and configuring it. For example, the following code retrieved all songs from the data source:
using MediaService.Objects;
using MediaService.Data;
using MediaService.Data.Accessors;
namespace MediaService.Web {
public class Media {
public Song[] GetSongs() {
return Dalc.Song.GetSongs();
} /* GetSongs */
With this data layer, all details about data access are contained in the real data implementations, while nowhere else there is specific data source code. The entire application works on data objects, which implement the data interfaces, while under the hood, the correct data source is selected through the configuration file.
The Pocket PC I recently received was completely in French, so I figured '
I'll just change this to English'.
It can't be that hard, can it? Apparently it was trickier then I thought.
The Pocket PC has the OS in it's ROM, and it has limited ROM, so no multilanguages in there.
It quickly became obvious to me the ROM had to be flashed with an English version, but where to get it?
I didn't buy the Pocket PC, so asking Dell to give me an English one probably would fail, and from various messageboards I discovered they won't do it anyway.
So, where to get it? From the Dell site I guessed, in the download section there was an English update for Windows Mobile 2003 Second Edition.
Since this update just flashes the ROM and puts the new version in it, I guessed this was ok.
But when trying to flash it, it started complaining about being the