views:

242

answers:

2

I have a rather peculiar data source I have to work with (an interface to an accounting application actually). While it is pretty powerful, I have to jump through pretty many hoops to get the data I want out of it. For example, if I want to get the contents of a table and specify which columns it should return, I have to iterate through the .Columns collection and call .SetVisible() to the ones I want.

Currently we have a method that wraps around this and allows us to specify things in an more simple way, but the parameter list to that function is growing fast, and most of the time we only need to specify a few of them when we call it. In short - it's an inflexible solution.

The first solution that came to my mind was something like this:

DataTable result = DataSourceWrapper.StartQuery("TableName")
    .SetVisibleColumns("Col1", "Col2", "Col3")
    .SetCriteria("CriteriaName", "Param1Name", CriteriaParam1, "Param2Name", CriteriaParam2)
    .SetFilter("Col4 = ? AND Col5 = ?", FilterParam1, FilterParam2)
    .SetReportParams("Param1Name", ReportParam1, "Param2Name", ReportParam2)
    .Execute();

Criteria, Filters and ReportParams are some things specific to the application and I will not discuss them here. But the general idea is like this. It's practically analogous to calling a method, except you can choose which parameters to specify (by calling specific methods) and have a bit more IntelliSense assistance. And you can also play with the order of the method calls.

Note that SetFilter() has an expression to parse. This is another thing that the DataSource makes difficult - it can process expressions quite nice, but you have to pass them as a tree of special objects, which again is pretty lengthy to write. In a previous question I asked for help on parsing such expressions. The current wrapper-method has a homebrew expression parser, which can parse simple expressions, but I thought of making the support for them more complete.

In that question, the Irony project was suggested. After checking it out I decided that it was indeed suited for this need. But after a while it dawned on me, that it was even more powerful than that. Why not make my own query language suited for exactly this task? The above would then look like:

DataTable result = DataSourceWrapper.Query(@"
    SELECT Col1, Col2, Col3
    FROM TableName
    WITH CRITERIA CriteriaName(Param1Name={0}, Param2Name={1})
    WITH REPORTPARAMS (Param1Name={2}, Param2Name={3}
    WHERE Col4 = {4} AND Col5 = {5}",
    CriteriaParam1, CriteriaParam2,
    ReportParam1, ReportParam2,
    FilterParam1, FilterParam2
);

But... would this not be an overkill? What are the pros and cons of either approach? What I see is:

Pro DSL:

  • query more concise;

Pro Methods:

  • More IntelliSense support;
  • Method names/parameter names (and comments) make less need for documentation (DSL will have to be documented thoroughly);
  • Might be quicker to create? I've never created my own DSL, so I don't know how much work that is. Irony seems to take a lot of the burden from my shoulders, but how much is still left there?

Added: To clarify, both approaches will be used only by coders. External people and business analysts will not use it.

A: 

I'd opt for DSL approach.

reasons: 1 it's closer to the domain experts, from personal experience, the BAs like something in the DSL forms, it breaks the communication barrier between the business minded people and the developers, and makes writing technical specifications much easier 2 by using a tool like Irony, your DSL will be well defined and a good candidate for test driven development 3 implementing DSL does require a bit of initial learning on the developers (i used javacc, which is even less friendly than Irony) but once you grasp the ideas, it becomes easy, and your code becomes quite generic because you off-load the logic into DSL.

+2  A: 

You have to be careful about what you call a DSL. See Martin Fowler's Bliki post on DSL. Your example of the method chaining is very close to an internal DSL. Modify it slightly and it is:

DataTable result = DataSourceWrapper.Query("TableName")
    .With(new Columns("Col1", "Col2", "Col3"))
    .Where(new AndCritera("CriteriaName",
        new Criterion("Param1Name", CriteriaParam1),
        new Criterion("Param2Name", CriteriaParam2))
    .Filter(
        new Filter("Col4").Equals(FilterParam1),
        new Filter("Col5").Equals(FilterParam2))
    .With(
        new ReportParam("Param1Name", ReportParam1),
        new ReportParam("Param2Name", ReportParam2));

That being said, this is still very much within the C# realm, and only programmers will be able to write these queries. If your requirements are pushing you toward making queries available to non-programmers, then you might consider undergoing the effort of creating an external DSL as you specified in the second example.

Michael Meadows
No, this will only be for internal use of us, programmers. And, yes - that's my point exactly. I'm so close to an actual DSL, that the next step would be to make it. The whole point of this is to make the code easier to write, so DSL kinda wins in that aspect (it's even shorter than what you wrote here), but what worries me is how long it will take me to create it. And perhaps there are other obstacles that I do not know of.
Vilx-
I'd suggest that there's no added value in creating a fully external DSL if the only consumers will be C# programmers. It really ends up being a waste of time in that case since programmers can easily interact with your internal DSL using Visual Studio.
Michael Meadows