This evening I continued my journey with the C# Generics.
I refactored what I described in my post “DataAccessLayer.FindAll(PublishedBy(Author))
“ to be able to use the Design
Pattern Interpreter
.

The result in the ASPX code behind file is the following:

1 AndSpec spec = new AndSpec(
2 new AndSpec(
3 new PublishedSpec(),
4 new BeforeDateSpec(DateTime.Parse(“01/01/2005”))
5 ),
6 new OrSpec(
7 new AuthorSpec(“Mathieu Kempé”),
8 new AuthorSpec(“Laurent Kempé”)
9 )
10 );
11
12 DataAccessor

articles = new DataAccessor
(“GetArticles”);
13 GridView3.DataSource = articles.FindAll(Matching(spec));
14 GridView3.DataBind();

With the unique Matching Predicate, replacing all the other Predicate:

1 protected Predicate

Matching(Spec spec)
2 {
3 return delegate(Article a)
4 {
5 return spec.isSatisfiedBy(a);
6 };
7 }

To achieve this I first added two more properties to my Entity, Article. I
did not added new constructors because those two properties are not
mandatory:

1 private DateTime datePublished;
2
3 public DateTime DatePublished
4 {
5 get { return datePublished; }
6 set { datePublished = value; }
7 }
8
9 private DateTime dateModified;
10
11 public DateTime DateModified
12 {
13 get { return dateModified; }
14 set { dateModified = value; }
15 }

Then I modified my Data Access Layer to have it a bit more generic, starting with the interface :

1 interface IDataAccess
2 {
3 List GetAll();
4
5 List FindAll(Predicate match);
6 }
7

I removed the method “T Get(Guid uuid)” and “T Get(string uuid)” because that can be easily expressed with a Predicate.

I made a Generic implementation of my Data Access through a DataAccessor class:

1 namespace TechHeadBrothers.Portal.DAL
2 {
3 public class DataAccessor : IDataAccess where T : new()
4 {
5 List list = null;
6
7 public DataAccessor(string sp)
8 {
9 readFromDatabase(sp);
10 }
11
12 #region Protected Methods
13
14 protected void readFromDatabase(string sp)
15 {
16 // Create Instance of Connection and Command Object
17 SqlConnection myConnection =
18 new SqlConnection(ConfigurationManager.ConnectionStrings[“TechHeadBrothers”].ConnectionString);
19
20 SqlDataAdapter myCommand = new SqlDataAdapter(sp, myConnection);
21
22 // Mark the Command as a SPROC
23 myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
24
25 // Create and Fill the DataSet
26 DataSet ds = new DataSet();
27 myCommand.Fill(ds);
28
29 myConnection.Close();
30
31 this.list = DataAdapterFactory.createAdapter().Adapt(ds.Tables[0]);
32 }
33
34 #endregion
35
36 #region IDataAccess Members
37
38 public List GetAll()
39 {
40 return list;
41 }
42
43 public List FindAll(Predicate match)
44 {
45 return list.FindAll(match);
46 }
47
48 #endregion
49 }
50 }

I had to implement an Adapter to convert the data from the Database representation to the Entity :

1 abstract class DataAdapter
2 {
3 public List Adapt(DataTable table)
4 {
5 List list = new List(table.Rows.Count);
6
7 foreach (DataRow row in table.Rows)
8 {
9 list.Add(this.Adapt(row));
10 }
11
12 return list;
13 }
14
15 public abstract T Adapt(DataRow row);
16 }

And a Factory :

1 class DataAdapterFactory
2 {
3 public static DataAdapter createAdapter() where T : new()
4 {
5 string name = new T().GetType().Name.ToLower();
6
7 if ( name == “article”)
8 return new ArticleAdapter() as DataAdapter;
9 else if ( name == “author”)
10 return new AuthorAdapter() as DataAdapter;
11
12 return null;
13 }
14 }

Here is the concrete implementation for the Article Entity Adapter:

1 class ArticleAdapter : DataAdapter


2 {
3 public override Article Adapt(DataRow row)
4 {
5 Article article = new Article((string)row[“Title”],
6 (string)row[“Description”],
7 (string)row[“Author”],
8 (bool)row[“isPublished”],
9 (Guid)row[“uuid”]);
10
11 if (row[“DatePublished”] != DBNull.Value)
12 {
13 article.DatePublished = (DateTime)row[“DatePublished”];
14 }
15
16 if (row[“DateModified”] != DBNull.Value)
17 {
18 article.DateModified = (DateTime)row[“DateModified”];
19 }
20
21 return article;
22 }
23 }

I made also a Business Layer generic class, but has it is just for the moment a wrapper around the DataAccessor, I will not show it.

And finally the Interpreter Design Pattern:

1 public abstract class Spec
2 {
3 public abstract bool isSatisfiedBy(Article article);
4 }

With the different concrete Specifications:

1 public class PublishedSpec : Spec
2 {
3 public PublishedSpec()
4 {
5 }
6
7 public override bool isSatisfiedBy(Article article)
8 {
9 return (article.isPublished == true);
10 }
11 }
12
13 public class BeforeDateSpec : Spec
14 {
15 private DateTime date;
16
17 public DateTime Date
18 {
19 get { return date; }
20 set { date = value; }
21 }
22
23 public BeforeDateSpec(DateTime date)
24 {
25 this.Date = date;
26 }
27
28 public override bool isSatisfiedBy(Article article)
29 {
30 return (article.DatePublished < this.Date);
31 }
32 }
33
34 public class AuthorSpec : Spec
35 {
36 private string author;
37
38 public string Author
39 {
40 get { return author;}
41 set { author = value; }
42 }
43
44 public AuthorSpec (string author)
45 {
46 this.Author = author;
47 }
48
49 public override bool isSatisfiedBy(Article article)
50 {
51 return (article.Author == this.Author);
52 }
53 }
54
55 public class NotSpec : Spec
56 {
57 private Spec specToNegate;
58
59 public NotSpec(Spec specToNegate)
60 {
61 this.specToNegate = specToNegate;
62 }
63
64 public override bool isSatisfiedBy(Article article)
65 {
66 return !specToNegate.isSatisfiedBy(article);
67 }
68 }
69
70 public class AndSpec : Spec
71 {
72 private Spec augend;
73 public Spec Augend
74 {
75 get { return augend;}
76 }
77
78 private Spec addend;
79 public Spec Addend
80 {
81 get { return addend;}
82 }
83
84 public AndSpec (Spec augend, Spec addend)
85 {
86 this.augend = augend;
87 this.addend = addend;
88 }
89
90 public override bool isSatisfiedBy(Article article)
91 {
92 return Augend.isSatisfiedBy(article) && Addend.isSatisfiedBy(article);
93 }
94 }
95
96 public class OrSpec : Spec
97 {
98 private Spec augend;
99 public Spec Augend
100 {
101 get { return augend; }
102 }
103
104 private Spec addend;
105 public Spec Addend
106 {
107 get { return addend; }
108 }
109
110 public OrSpec(Spec augend, Spec addend)
111 {
112 this.augend = augend;
113 this.addend = addend;
114 }
115
116 public override bool isSatisfiedBy(Article article)
117 {
118 return Augend.isSatisfiedBy(article) || Addend.isSatisfiedBy(article);
119 }
120 }