Microsoft recently announced their WebMatrix product. The application is meant to enable hobbyist web site owners to easily create dynamic web pages that have the ability to interact with data. WebMatrix is not meant to compete with Microsoft Visual Studio, which is targeted towards enterprise application development, rather it is intended to provide a development environment for a Windows alternative to the LAMP stack.
An intro to one of the productivity enhancements was covered on David Fowler's blog. The post dealt with a new .Net data access wrapper library from Microsoft, aptly named Microsoft.Data.dll (note this is not part of the .Net framework). Unfortunately for the author, the intended hobbyist audience turned out to not be the group actually subscribing to the blog. Instead, a backlash of comments came in from the professional development pundits thrashing the example posted on the blog, like this one.
The example was intended to be simple to show the power of the library and the reduction in complexity for the hobbyist when dealing with data access. Now Microsoft is in the business of getting people onto their platform to generate revenue and they often do over simplify code examples in order to illustrate a point. Also, to be fair, without the right education a hobbyist can get into trouble when they aren't advised of potential security risks.As such, the new code probably should have looked a litte more like this:
using (var db = Database.OpenFile("Northwind")) {
foreach (var product in db.Query(
"select * from products where UnitsInStock < @0", 20)) {
Response.Write(string.Format({0} {1}<br />),
Server.HtmlEncode(product.ProductName), product.UnitsInStock);
}
}
Sure it's deceivingly simple, but if all a person (note I didn't say developer) needs to do is get out a quick and dirty, read-only report of items with low inventory then I would argue that this provides more short term value than over-engineering a complex solution, in the event the client *might* need to refactor the report into an N-tier solution someday. If you need to do that in WebMatrix then your page would probably look more like this:
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Configuration" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Page Language="C#" %>
<script runat="server">
// WebApp
protected void Page_Load(object sender, EventArgs e)
{
InventoryServiceClient client = new InventoryServiceClient();
// assume global error handling since this is all one layer
List<ClientProduct> productList = client.GetProductsForReorder();
foreach (ClientProduct product in productList)
{
Response.Write(string.Format("{0} {1}<br />",
Server.HtmlEncode(product.ProductName), product.UnitsInStock));
}
}
// ServiceClient
internal class InventoryServiceClient {
public List<ClientProduct> GetProductsForReorder() {
List<ClientProduct> productList = new List<ClientProduct>();
IInventoryDataService inventorySvc = new InventoryDataService();
List<ProductDataContract> serviceProductList =
inventorySvc.GetProductsForReorder();
foreach (ProductDataContract serverProduct in serviceProductList) {
productList.Add(new ClientProduct() {
ProductName = serverProduct.ProductName,
UnitsInStock = serverProduct.UnitsInStock
});
}
return productList;
}
}
internal class ClientProduct {
public string ProductName { get; set; }
public int UnitsInStock { get; set; }
}
// ServiceContracts
public interface IInventoryDataService {
List<ProductDataContract> GetProductsForReorder();
}
public interface IInventoryBusinessService {
int GetReorderPoint();
}
public interface IProductRepository {
List<ProductDataContract> GetProductsForReorder(int reorderPoint);
}
public class ProductDataContract {
public string ProductName { get; set; }
public int UnitsInStock { get; set; }
}
// BusinessAccess
public sealed class InventoryBusinessService : IInventoryBusinessService {
private const string REORDER_POINT = "ReorderPoint";
public int GetReorderPoint() {
return int.Parse(ConfigurationManager.AppSettings[REORDER_POINT]);
}
}
// DataAccess
public sealed class InventoryDataService : IInventoryDataService {
public List<ProductDataContract> GetProductsForReorder() {
IInventoryBusinessService inventoryBusiness = new InventoryBusinessService();
IProductRepository productRepository = new SqlProductRepository();
return productRepository.GetProductsForReorder(
inventoryBusiness.GetReorderPoint());
}
}
internal sealed class SqlProductRepository : IProductRepository {
private string _connectionString =
ConfigurationManager.ConnectionStrings["DataAccess"].ConnectionString;
public List<ProductDataContract> GetProductsForReorder(int reorderPoint)
{
ProductDataAccess productData = new ProductDataAccess();
List<ProductDataContract> productList = new List<ProductDataContract>();
using (SqlConnection connection = new SqlConnection(_connectionString)) {
connection.Open();
productList = productData.GetProductsForReorder(connection, reorderPoint);
}
return productList;
}
}
internal sealed class ProductDataAccess {
private const string LOW_INVENTORY_SQL =
"select ProductName, UnitsInStock from products where UnitsInStock < @reorderPoint";
private const int PRODUCT_NAME_POSITION = 0;
private const int UNITS_IN_STOCK_POSITION = 1;
public List<ProductDataContract> GetProductsForReorder(
SqlConnection connection, int reorderPoint)
{
List<ProductDataContract> productList = new List<ProductDataContract>();
using (SqlCommand command = new SqlCommand(LOW_INVENTORY_SQL, connection)) {
command.Parameters.Add(new SqlParameter() { ParameterName = "@reorderPoint",
DbType = System.Data.DbType.String, Value = reorderPoint
});
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
productList.Add(new ProductDataContract() {
ProductName = reader.IsDBNull(PRODUCT_NAME_POSITION) ?
null : reader.GetString(PRODUCT_NAME_POSITION).TrimEnd(),
UnitsInStock = reader.GetInt32(UNITS_IN_STOCK_POSITION)
});}}}
return productList;
}
}
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server"><title>Low Inventory Report</title></head>
<body><form id="form1" runat="server"><div></div></form></body>
</html>
We encourage you to share your comments on this post. Comments are moderated and will be reviewed and posted as promptly as possible during regular business hours.
To ensure your comment is published, please follow our community guidelines.