OK, readers - similar to the contest at , I'm giving away a Gmail account to the funniest joke or story that gets posted here. Of course, this is 100% subjective but my redership is modest, so you have a great chance of winning the account. I alone will choose the winner based on how hard/long you make me laugh. Thank Sergio for the inspiration. If you haven't checked out , you're missing out on a real gem - one of the best blogs on the net. Go check it out!
Sam Gentile states it perfectly . Microsoft's trying to skimp on including the Unit Testing tools in every Whidbey version, which makes zero sense to me. Unit Testing is the more sophisticated cousin of Debug.Assert. We wouldn't reserve that for a special version, would we?!
A hybrid of Proxy and .
“The proxy pattern makes the clients of a component communicate with a representative rather than to the component itself. Introducing such a placeholder can serve many purposes, including enhanced efficiency, easier access and protection from unauthorized access.”
The Decorator pattern serves to attach “additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.”
public interface IHotelGateway { IHotel GetHotel(string Destination, string HotelCode); } public class HotelCacheGateway: IHotelGateway { private IHotelGateway store = null; public HotelCacheGateway(IHotelGateway Store) { store = Store; } public IHotel GetHotel(string Destination, string HotelCode) { IHotel hotel = Cache[GenerateKey(Destination, HotelCode)] as IHotel; if (null != hotel) return hotel; hotel = store.GetHotel(Destination, HotelCode); if (null != hotel) Cache[GenerateKey(Destination, HotelCode)] = hotel; return hotel; } }
Then, instead of asking the Data Access Layer (DAL) for an object directly, the client code can ask the Decorating Proxy which will either
retrieve the object from a cache or delegate the call to the DAL.
IHotelGateway proxy = null, dal = null; dal = new SqlHotelGateway(connection_string); proxy = new HotelCacheGateway(dal); IHotel hotel = proxy.GetHotel(destination, hotelcode);
Or, better still, the creation of the Proxy and DAL can be encapsulated in a Factory Method which keeps the client from having to even know about the Proxy.
public class HotelGatewayFactory { private const string ConnectionString = "..."; public static IHotelGateway Create() { IHotelGateway proxy = null, dal = null; dal = new SqlHotelGateway(ConnectionString); proxy = new HotelCacheGateway(dal); return proxy; } } IHotelGateway gateway = HotelGatewayFactory.Create(); IHotel hotel = gateway.GetHotel(destination, hotelcode);
using System; using System.Web; using System.Web.Caching; // minimal implementation - a better implementation would allow for dependency and expiration control public class CacheManager { private static CacheManager singleton = null; private CacheManager() { } public static CacheManager Instance { get { if (null == singleton) singleton = new CacheManager(); return singleton; } } public static void SetItem(string key, object value) { HttpContext.Current.Cache[key] = value; } public static object GetItem(string key) { return HttpContext.Current.Cache[key]; } } using System; public interface IHotelGateway { IHotel GetHotel(string Destination, string HotelCode); } using System; /* -- note lack of dependency on System.Web.Caching -- */ public class HotelCacheGateway: IHotelGateway { private IHotelGateway store = null; public HotelCacheGateway(IHotelGateway Store) { store = Store; } public IHotel GetHotel(string Destination, string HotelCode) { IHotel hotel = CacheManager.Instance.GetItem(GenerateKey(Destination, HotelCode)) as IHotel; if (null != hotel) return hotel; hotel = store.GetHotel(Destination, HotelCode); if (null != hotel) CacheManager.Instance.SetItem(GenerateKey(Destination, HotelCode), hotel); return hotel; } private string GenerateKey(string Destination, string HotelCode) { return string.Format("Hotel:{1}-{0}", Destination, HotelCode); } } using System; using System.Data; using System.Data.SqlClient; public class SqlHotelGateway: IHotelGateway { private string ConnectionString = null; public SqlHotelGateway(string ConnString) { ConnectionString = ConnString; } public IHotel GetHotel(string Destination, string HotelCode) { IHotel hotel = new Hotel(Destination, HotelCode); using (SqlConnection db = new SqlConnection(ConnectionString)) { SqlCommand sp = new SqlCommand(db); sp.CommandType = CommandType.StoredProcedure; sp.CommandText = "usp_GetHotel"; sp.Parameters.Add(new SqlParameter("", Destination)); sp.Parameters.Add(new SqlParameter("", HotelCode)); using (SqlDataReader rdr = sp.ExecuteReader()) { while (rdr.Read()) { hotel.Description = rdr["Description"].ToString(); hotel.SetContact(rdr["ContactName"].ToString(), rdr["ContactEmail"].ToString()); hotel.RoomCount = DBNull.Value == rdr["NumberOfRooms"] ? -1 : int.Parse(rdr["NumberOfRooms"]); } } } return hotel; } } using System; public class HotelGatewayFactory { private const string ConnectionString = "..."; public static IHotelGateway Create() { IHotelGateway proxy = null, dal = null; dal = new SqlHotelGateway(ConnectionString); proxy = new HotelCacheGateway(dal); return proxy; } } // Client code IHotelGateway gateway = HotelGatewayFactory.Create(); IHotel hotel = gateway.GetHotel(destination, hotelcode);The main thing to notice about the code above is that the dependencies on specific parts of the framework (like System.Web and System.Data) are limited only to those objects that need to work with them directly. This makes swapping out implementations easier because the dependenices are localized. Changing the CacheManager to work with the file system would not affect client code one bit. Likewise, a migration to an Oracle backend would only affect one class.
Of course, this also assumes certain things about how you package the classes into assemblies. For instance, if you package these classes into one monolithic assembly, then while most code maintenance benefits remain, you will force a recompilation every time you add a new Decorating Proxy that the Factory class needs to know about. This pattern forces you to separate the Factory into a separate assembly so that you minimize the impact of changes. Of course, having an assembly with one class — especially such a small one — is lame, so I would advocate packaging all or several factory classes together into an assembly that, in essence, defines the API for the application. If this sounds familiar, it’s probably because it sounds a lot like Martin Fowler’s Service Layer pattern from , albeit at a lower level. Service Layer is more of a UI layer pattern. This pattern is more of a Data Access Service Layer which defines a common interface for accessing data without requiring the caller to care about implementation details like whether or not the data is being retrieved from a cache or the backing data store.
For those who were subscribed to my old blog, this is a bit of a rehashing so forgive me. I'm in the middle of building a Web Service where I have to return some information about hotels. The data is stored in SQL Server, but I'm focusing on getting the messaging interface done before delving into the actual data retrieval. The web service has the requirement that it must conform exactly to the interface of an existing web service from one of our business partners. That way, our partners can go to the web.config file and swap out their implementation for ours. Turns out the syntax and semantics of their messages are pretty quirky (read crappy). For example, rather than making use of the SOAP Fault facility, their message format defines an
Anyway, I digress. The point is that I want to test that the message structure returned from the web service conforms to their specifications under normal conditions and exception conditions. I know I only need to pass the SQL stored procedure (which I haven't written yet) two parameters: DestinationCode and HotelCode. What I really want to do is to create an interface that defines the operation like so:
This way, I can build myself a mock implementation of the IHotelGateway interface that returns some sample data right away, letting me concentrate on getting the interface right before I worry about the implementation details. This has the benefit of allowing the business partner to test their client against my web service without having to wait for me to do all the wiring against the database. The next problem I anticipate is that there will be several places in my local code that look like this:
Update: Dug up the original thread from Chris Anderson's weblog.
Sometimes (way too often) you'll come across an error when developing ASP.NET applications. Here are the symptoms: one minute you're coding/debugging/testing no problem. Next minute, you get the dreaded yellow, white and red unhandled exception screen. The page is entitled Configuration Error and the highlighted line says:
The parser error is Access is denied: '[some assembly]'. Having struggled against this insidious beast time and time again, I am pleased to announce that I stumbled upon a solution in the Infragistics Knowledge Base while Googling for a solution! Seems that the Indexing Service isn't playing nice with the Temporary ASP.NET Assemblies.
Check out the work-arounds here.
According to Technorati, I'm ranked 106,422 of 2,681,383. Let me analyze what this means to me. It sucks to be in the 75th percentile when you're used to being in the 98th or better. My blog currently has 6 inbound blogs. I'm very flattered that 6 people have taken the time to add me to their blogrolls. In the blogosphere, blogroll links are a vote of confidence and credibility. So, thanks to (in no particular order): , Chris Andersen, , Richard Tallent, and Jeff Julian.
Now, stopping to consider this for a moment it means that 75% of all blogs watched by Technorati have less than 6 inbound permanent links. This seems to confirm that most of us are writing because we like the sound of our own voice the look of our own text.
Now playing: Black Eyed Peas - Let's Get It Started (Spike Mix) (Elephunk)
BrettK (a.k.a. x002548), a member of the SQL Team, posted an entry entitled Surrogate Keys...the Devil's spawn (OK Not really). As Brett acknowledges, this topic always sparks a heated debate. He listed 6 cons of working with surrogate keys, but only one pro. He also asked what other situations might merit the use of a surrogate key. Here's my take on one:
Surrogate keys can be plenty useful when it's a pain in the neck to have to carry around 5+ fields to do a join on the natural key. For example imagine an entity, Party where you have a single ID (datatype irrelevant) that serves as the PK. You want to model the relationships between two Partys over time. You create an entity PartyRelationship that contains both Party's id's and the start and end dates for the relationship. However, two parties can have several relationships. You may be a Microsoft employee, but also a customer. So let's assume a RelationshipType entity that has a FromPartyRole and ToPartyRole and a Description. To capture all these requirements, your PartyRelationship entity now has FromPartyId, FromPartyRole, ToPartyId, ToPartyRole, StartDate as it's "natural" PK. EndDate is not needed in the PK. With me so far? Next, assume you have entities that relate to PartyRelationship (think of Agreements perhaps) and you can see that you're going to have to carry around these five fields in your join. I would much prefer to have a clustered, unique key on the five fields and have a surrogate key, PartyRelationship Id to simplify my queries. As for where I pulled this hypothetical from...SQL 2000 Enterprise Edition ships with the Silverston Data Model in its samples directory.
An obligatory post for a blog entitled 'xml-blog': get a free copy of Altova's Xml Spy 2004 Home Edition here.
At some point you may very well need to call code like the following from your test class library project:
string myValue = ConfigurationSettings.AppSettings["Data.Key"];
Subsequently, when running NUnit you may find that no value is returned from the expression above. This is because NUnit is looking for an xml configuration file with the same name as the assembly + .config in the directory the loaded assembly is running from.
Example: Suppose your assembly is located at C:\Code\My Project\MyAssembly.dll For the ConfigurationSettings.AppSettings call to work properly, you need to have a file at c:\Code\My Project\MyAssembly.dll.config.
Well, an easy way to automate the creation of this file from Visual Studio is to right-click on the project and choose properties from the context menu. Then, under common properties -> build events, add set the following value for the Post-Build Event Command Line:
copy $(ProjectDir)App.config $(TargetPath).config
This will make Visual Studio copy the App.Config file into the appropriate bin\Debug or bin\Release directory (depending on the current configuration) so NUnit will find the file. Happy Unit Testing!