December 2005 - Posts
I've been playing with Ruby on Rails, and there's a lot to like.
There are some things that we won't be able to get while working in C#, but we could build some stuff to get closer. I know there are some people working on porting RoR to .NET but I prefer an approach that instead of porting it, it tries to take advantage of ASP.NET even if the framework is not the same.
I think the ASP.NET compilation model (which actually compiles everything in runtime), Build Providers, DLinQ and C# 3.0 are a good start, but we need more.
We need much better support for generics in the framework. The generics implementation is good but ASP.NET does not work well with them. For example, we need to be able to define asp.net controls that use generic types and use them in the .aspx pages.
I was thinking on implementing RoR scaffolding. It can be done, and as you can use ASP.NET controls you have a lot of power there (i.e, I can turn on paging by setting a property, use skins/themes, master pages, etc). The problem is that I need to generate the Page code itself, and that is not easy and it's not easy to extend/maintain.
What I want to have is a build provider that generates an ASPX page. I'm not sure if it can be built today, as the build provider needs to generate a CodeDom. ASPX pages have a build provider. I need to chain the build providers. If I can do that, the it will be much easier to implement scaffolding and also it will be very easy to generate the scaffolding static code so you can later customize it. I'm just thinking that it might be possible... I'll give it a try ;).
In the pnp Summit West there was a 'GotDotNet CodeSlam'.
I've been thinking that DLinQ + ASP.NET 2.0 Build Providers could give us a very easy, rubyonrails-esque kind of programming, and driving back to the hotel I thought of the obvious way of mixing both.
What I built for the CodeSlam (by the way, people did not seem to like it much, as it did not win any prize ;), but I still think it's cool), is a Build provider for DLinQ. Basically, you add a file with a .linq extension to the App_code folder, with the following format:
<
DLinQGenerator>
<connectionString>server=.;trusted_connection=yes;database=northwind</connectionString>
<namespace>DataAccess</namespace>
<className>Northwind</className>
</DLinQGenerator> Then, without any further work I can write an aspx page like:
<% DataAccess.Northwind db = new DataAccess.Northwind();
var one = from customer in db.Customers
select new {customer.ContactName, customer.Phone}; %>
<table>
<% foreach (var x in one) { %>
<tr>
<td> <%= x.ContactName %></td>
<td> <%= x.Phone %></td>
</tr>
<% } %>
</table> The build provider generates a CodeDom that has the DLinQ classes for the database, and ASP.NET gives you intellisense... cool! The only trick is that to generate the CodeDom, I dissasembled the 'sqlmetal' tool distributed with LinQ so I could use some internal methods that already built the CodeDom tree, so the implementation was actually quite easy.
Note that this could overcome some of the perceived limitations on DLinQ, for example, even if you are using attributes in the classes to make DLinQ work, you could generate different attributes depending on the database you want to use, or some other strategy.
With that you can display data, but what about updating? To have two-way databinding over a moderatley interesting data source in ASP.NET you need to implement your own ASP.NET DataSource, so I wrote one for DLinQ classes (the source is in the gotdotnet workspace).
The main problem is that I need to use generics to create the DataSource control to be able to use DLinQ in it (I tried without generics and I did not find a way to do it, but if you find it let me know). If you use generics in an ASP.NET control then you can not create it in the .aspx page! :( :( :(. So, instead of having a nice declarative way to create it, you need to write code:
DLinQDataSource<DataAccess.Products> dataSource = new DLinQDataSource<DataAccess.Products>(); dataSource.ID =
"dataSource1";
dataSource.Table = productList;
dataSource.DataContext = db; Controls.Add(dataSource);
this.GridView1.DataSourceID = "dataSource1"; If you update/insert/update with the GridView, it will use the DLinQ classes to persist the data. It will keep the DataContext in the session or viewstate to be able to update it. The DataSource implementation is kind of interesting as you need to manually build ExpressionTrees to use the DataSource information sent in the ExecuteSelect/Update/Insert/Delete methods to be able to retrieve the right instance from the DataContext and then submit the changes.
Even if it's hard to believe, ASP.NET 2.0 DataSources don't have good support for typed DataSets. I blogged about that here, and now I wrote a DataSetDataSource to overcome this limitation. Read about it here
I like the idea of TDD. I think it could be a good fit for a part of the kind of development I do, but I still don't have the discipline it takes to start doing it.
On the other hand, suppose we could write something like:
throw NotEnoughInventoryException if Product.Inventory < 0;
and we had a framework that ensured that the constraint will be checked in the right places (the UI, the business logic layer, the database, whatever). Let's suppose that framework is already built and tested by whoever built it.
Now suppose I want to write this code in a TDD way. I will start writing a test that will set the product Inventory in -1 and try to save it. The test will first fail, then I'll write my implementation code, and then it will succeed.
But I'm actually a little (OK, more than a little) lazy, and I want the same test to work for the Win UI, the Web UI, the Business Logic Layer, the database, whatever, so I write a framework that let's me write something like:
throw NotEnoughInventoryException if Product.Inventory < 0;
and generates the code for the tests.
Now I start feeling a little stupid, as I'm writing the same code twice, one for the test and another for the implementation.
I can't help thinking that if we could write code at the right level of abstraction, then we won't need TDD or even Unit Testing. No one (I hope) writes a test to check if a statement like 'a = 1' works OK.
That's probably why I feel DSLs are much more interesting than TDD.
If we had a language that lets us express ourselves in the right level of abstraction, would we need TDD or Unit Testing?
Any TDD converted can help me here?
DeKlarit will be sponsoring the Patterns and Practices Summit next week in Redmond. I'll be there doing a lunch time session showing what we are doing with the Composite Application Block.
See you there!
More Posts