Please read Of Interfaces (1of3) and Of Interfaces (2of3) before reading this blog posting as it is the last in a series of blog postings. In my last refactoring, I will add a fourth project called InterfaceExample.Database and it too will contain a PersonRepository and reference the core. The UI will reference InterfaceExample.Database instead of InterfaceExample.FileIO (I drop the reference as this project is now depreciated.) and the PersonRepository of InterfaceExample.Database will be wired up in this example to work with the IPersonRepository interface in the core. This switch out should prove fairly painless, thus giving an exposition of how interfaces may be blind to what feeds them.
What am I trying to do? This change represents a real world decision that one might make to change out the data layer, to replace the element that is driving content with something new. One might look at an app that had simple data populated by flat files and decide that the data really needs to be more sophisticated and hence be driven by an actual database. Such a move would make CRUD functions that are to come in version two of our web site a lot easier to approach. I mentioned that a three-layered approach would have been a bad idea in the prior posting, and I hope you can see now how a separation of concerns, teasing the external dependencies out form the UI and core, allow for this switch to be easy where it would otherwise (in a three-layered approach) be painful. There is really very little to show off in this blog posting compared to my prior two postings, but let's go through it now. I made a database table, gave it some rows, and then wrote two stored procedures for fishing data back out of it with this SQL:
ALTER TABLE dbo.Person ADD CONSTRAINT
PK_Person PRIMARY KEY CLUSTERED
(
Id
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE dbo.Person SET (LOCK_ESCALATION = TABLE)
INSERT INTO Person (Id, Name)
Values ('58bfe64e-893a-44f0-b8d2-9e6a007bc0fa','Thomas J. Watson Jr.')
INSERT INTO Person (Id, Name)
Values ('1ba51989-84fb-4a61-8c35-9e6a007bc0fb','T. Vincent Learson')
INSERT INTO Person (Id, Name)
Values ('850cbe61-4535-4861-bb10-9e6a007bc102','Frank Cary')
INSERT INTO Person (Id, Name)
Values ('90edbc51-b2b2-4d8d-915b-9e6a007bc0f9','John Opel')
INSERT INTO Person (Id, Name)
Values ('281872d1-4a1e-4d0c-8dc2-9e6a007bc101','John Akers')
INSERT INTO Person (Id, Name)
Values ('71c2305a-da77-4dbf-842a-9e6a007bc0fe','Louis V. Gerstner Jr.')
INSERT INTO Person (Id, Name)
Values ('73a15177-38af-495f-987f-9e6a007bc0fd','Samuel J. Palmisano')
COMMIT
GO
CREATE PROCEDURE GetAllPeople
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM Person
END
GO
CREATE PROCEDURE GetPeopleNotNamedJohn
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM Person WHERE Name Not Like '%John%'
END
GO
The new PersonRepository:
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using InterfaceExample.Core;
namespace InterfaceExample.Database
{
public class PersonRepository : IPersonRepository
{
public List<Person> GetAllPeople()
{
return GetPeopleFromStoredProcedure("GetAllPeople");
}
public List<Person> GetPeopleNotNamedJohn()
{
return GetPeopleFromStoredProcedure("GetPeopleNotNamedJohn");
}
private static List<Person>
GetPeopleFromStoredProcedure(string storedProcedureName)
{
string key = "Data Source=foo;Initial Catalog=bar;
Persist Security Info=True;User ID=baz;Password=qux";
SqlConnection sqlConnection = new SqlConnection(key);
SqlCommand sqlCommand = new SqlCommand();
SqlDataReader reader;
sqlCommand.CommandText = storedProcedureName;
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Connection = sqlConnection;
sqlConnection.Open();
reader = sqlCommand.ExecuteReader();
DataTable dataTable = new DataTable();
dataTable.Load(reader);
sqlConnection.Close();
List<Person> people = (from DataRow dataRow in dataTable.Rows
select new Person { Name = dataRow[1].ToString() }).ToList();
return people;
}
}
}
OK, there are only two existing pieces of code that need to change. One is StructureMap.config:
<StructureMap>
<PluginFamily Type="InterfaceExample.Core.IPersonRepository"
Assembly="InterfaceExample.Core" DefaultKey="Default">
<Plugin Assembly="InterfaceExample.Database"
Type="InterfaceExample.Database.PersonRepository" ConcreteKey="Default" />
</PluginFamily>
</StructureMap>
Finally, a using statement in the code behind of our web form changes:
using System;
using InterfaceExample.Core;
using InterfaceExample.Database;
using StructureMap;
namespace InterfaceExample
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ObjectFactory.Initialize(x =>
{
x.ForRequestedType<IPersonRepository>()
.TheDefaultIsConcreteType<PersonRepository>();
});
IPersonRepository personRepository =
ObjectFactory.GetInstance<IPersonRepository>();
Repeater1.DataSource = personRepository.GetPeopleNotNamedJohn();
Repeater1.DataBind();
}
}
}
No comments:
Post a Comment