Validating sending mail messages in smart use case unit tests

When building applications with the Adf framework, smart use cases are implemented in task classes. Quite regularly mail messages are sent from tasks. To do so we use the MailManager class. Using this class mail messages are usually build up as in the following code example.

image

To send mail messages, the MailManager plugs in an implementation of the IMailProvider interface. Currently, Adf provides two mail providers, the obvious implementation SmtpMailProvider, and the DummyMailProvider, which creates the mail messages and dumps it in a specified directory.

Smart use cases, implemented in task classes, are always unit tested. Here, each of the public methods of the tasks are tested using the Adf.Test libraries from the framework. A number of test methods is usually implemented to test all possible scenario’s of going through the smart use cases. In the following code example, such as method is shown.

image

In this example, the Init() method of the task is called. After it is called the test framework can perform any number of checks. In this example, all validations should succeed, and the Status property of the Account domain object should be set to Unverified. The last validation ViewIsActivated will check if the accompanying (web, Windows RT) page or Windows form is presented to the user.

Recently, we have added new features to also validate whether a task has sent the mail message it was supposed to send. To provide for this functionality, we’ve added a TestMailProvider to the Adf.Test libraries. In your test project, this mail provider needs to be plugged in to the MailManager, as follows in code (or in the app.config file).

image

This will ensure that any mail messages being send by the tasks, is sent through the TestMailProvider. The test mail provider will, similar to the DummyMailProvider, place mail messages in a specified directory, but foremost it will also notify the TestManager that a message was sent.

Next, when unit testing the method that either sends, or doesn’t send the mail messages, you will be able to verify this, as in the following code example.

image

You can use the MailIsSent or the MailIsNotSent methods for this means.

Reaching post-conditions in tasks

Implementing use cases in Adf.Net is covered by the task pattern. Each smart use case in the model is implemented as a descendant of the Task class in Adf.Net.

The task pattern consists of three major parts:

  • Starting the task, either using a parameterized Init() method, or the default Start() method.
  • After a task calls another task, and this second task is finished, control goes back to the calling task, using any of the specific ContinueFrom() methods, or the default Continue() method.
  • When a task reaches any of the post-conditions of a task, the task needs to be ended, usually through the Ok() or Cancel() method.

In this specific post I’ll look at reaching the post-conditions. In the pattern, the Task class itself acts as the layer super type and implements a number of method to end itself. A use case can have multiple post-conditions. Some of them are positive, some can be negative. And sometimes a specific case needs to be addressed.

When a reaches it positive post-condition, it is a best practices to end it using the Ok() method. This method takes a params object[] p, which allows you to pass back results from the task to the calling task, using the following signature.

public virtual void OK(params object[] p);

A similar method Cancel() exists for task that end in a negative post-condition, usually because the user cancels the interaction:

public virtual void Cancel(params object[] p);

But, Adf.Net also supplies a similar method Error(), to allow tasks to finish with an error, possibly technical.

Under the covers, these three methods call on another method, which is called Finish(). This method takes care of passing back an instance of TaskResult. This result also gets posted back to the calling task, so it knows how the called task has ended. As in the following example, you could also use Finish() yourself.

if (persoon.IsNullOrEmpty())
{
    ValidationManager.AddError(GEENPERSOONGESELECTEERD);
    Finish(TaskResult.Error);
}

Please note that the code above is equal to the following code.

if (persoon.IsNullOrEmpty())
{
    ValidationManager.AddError(GEENPERSOONGESELECTEERD);
    Error();
}

By default, TaskResult has the following values in Adf.Net: Ok, Cancel, Error.

However, as TaskResult is implemented using the descriptor pattern, additional project specific value can be added easily, by inheriting from TaskResult, and added the specific values. Thus you would be able to end your task in more project specific ways, such as in the code example below.

if (persoon.IsNullOrEmpty())
{
    ValidationManager.AddError(GEENPERSOONGESELECTEERD);
    Finish(Dashboard8TaskResult.Aborted);
}

A short notice about object relational mapping framework generated queries…

I guess object relational mapping is an accepted paradigm for exchanging data between an object oriented domain layer and underlying databases. For most applications object relational mapping is more than sufficient. And if not, perhaps command query responsibility segregation might contribute well to your solution.

Being a more than frequent user, this very short blog post is not meant to question either of these popular paradigms. However, I would like to make it clear that applying object relational mapping might not always lead to optimal solutions, especially from a database perspective. Most object relational mapping framework generate the underlying queries to the database, based on the domain layer and the associations between the domain objects in this layer. The quality of these generated queries is strongly dependent on the quality of the domain model and layer. If the domain model is complex, so will the generated queries be. Rubbish in is rubbish out.

image

So it is noteworthy how the framework you’re using maps associations and properties to SQL statements. Some frameworks may choose remarkable construct for this mapping resulting in hard and unreadable queries, or queries that aren’t very much optimized for the underlying data model. So this short blog post is just to make you notice.

The problem actually comes from the enormous amount of features a full-grown object relational mapping framework needs to implement. Basically, everything and the kitchen sink. Driven by the good but blind ambition to serve as many developers as they possibly can, in any case more than all other competitors, the framework developers implement as many features as the code in the framework can handle. And sometimes this feature bloating may lead to peculiar results, where the framework might still function, but the original goals of providing a lightweight, straightforward, easy-to-use framework to solve most common mapping problems have long been abandoned.

For developers using such frameworks I guess the most decent piece of advice I can give is: don’t go into the dark corners of domain modeling, as the solutions resulting may be hard to maintain and to extend. Use middle-of-the-road constructs, and prefer simpler, more generic out-of-the-box solutions to more specific and elaborate domain models.

I’ll leave with an example of a query which was generated by a frequently used object relational mapping framework in the .Net space. I’m quite sure the database will have a hard time executing this particular query.

And yes, the following is indeed a single select query. Go figure.

SELECT

[Extent1].[ID] AS [ID],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN ’2X’ WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1) AND([UnionAll2].[C6] IS NOT NULL)))) THEN ’2X0X’ WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THEN ’2X0X0X’ WHEN(([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN’2X1X’ ELSE ’2X1X0X’ END AS [C1],

[Extent1].[MAPReportId] AS [MAPReportId],

[Extent1].[PartnerNumber] AS [PartnerNumber],

[Extent1].[CapturedVia] AS [CapturedVia],

[Extent1].[Currency] AS [Currency],

[Extent1].[DataAsOf] AS [DataAsOf],

[Extent1].[DateApproved] AS [DateApproved],

[Extent1].[DateCreated] AS [DateCreated],

[Extent1].[DateProcessed] AS [DateProcessed],

[Extent1].[DateReturned] AS [DateReturned],

[Extent1].[DateSubmittedToRo] AS [DateSubmittedToRo],

[Extent1].[PeriodCovered] AS [PeriodCovered],

[Extent1].[ProcessGeneralComments] AS [ProcessGeneralComments],

[Extent1].[ReasonReturned] AS [ReasonReturned],

[Extent1].[StatusID] AS [StatusID],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[AverageLoanSize] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL))THEN [UnionAll1].[AverageLoanSize] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C2],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[AveragePortfolioOutstanding] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6]IS NOT NULL)) THEN [UnionAll1].[AveragePortfolioOutstanding] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT(([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C3],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[BoYPortfolioOutstanding] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] ISNOT NULL)) THEN [UnionAll1].[BoYPortfolioOutstanding] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT(([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C4],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[DepositsAmount] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL))THEN [UnionAll1].[DepositsAmount] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C5],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS int) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1) AND([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[EoYNrBorrowers] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THEN[UnionAll1].[EoYNrBorrowers] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS int) END AS [C6],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[EoYPortfolioOutstanding] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] ISNOT NULL)) THEN [UnionAll1].[EoYPortfolioOutstanding] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT(([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C7],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] =1) AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[MFIPortfolioSize] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOTNULL)) THEN [UnionAll1].[MFIPortfolioSize] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] =1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) END AS [C8],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS int) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1) AND([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[NrLoans] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THEN[UnionAll1].[NrLoans] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS int) END AS [C9],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[InvestmentOutstanding] WHEN (([UnionAll2].[C6] = 1) AND([UnionAll2].[C6] IS NOT NULL)) THEN [UnionAll1].[InvestmentOutstanding] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C10],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[LoanOutstanding] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] ISNOT NULL)) THEN [UnionAll1].[LoanOutstanding] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT(([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C11],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN [UnionAll1].[OutstandingExposure] WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOTNULL)) THEN [UnionAll1].[OutstandingExposure] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT(([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C12],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] =1) AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL))THEN [UnionAll2].[C1] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) END AS [C13],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THEN[UnionAll2].[C2] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6]IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C14],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THEN[UnionAll2].[C3] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6]IS NOT NULL)))) THEN CAST(NULL AS float) END AS [C15],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] =1) AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL))THEN [UnionAll2].[C4] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) END AS [C16],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS bit) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1) AND([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS bit) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THEN[UnionAll2].[C5] WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND ([UnionAll1].[C6]IS NOT NULL)))) THEN CAST(NULL AS bit) END AS [C17],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THENCAST(NULL AS float) WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN [UnionAll2].[Exposure] ELSE [UnionAll2].[Exposure] END AS [C18],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THENCAST(NULL AS float) WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN [UnionAll2].[InvestmentOutstanding] ELSE [UnionAll2].[InvestmentOutstanding]END AS [C19],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT (([UnionAll2].[C6] = 1)AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS float) WHEN (([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)) THENCAST(NULL AS float) WHEN (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] IS NOT NULL) AND ( NOT (([UnionAll1].[C6] = 1) AND([UnionAll1].[C6] IS NOT NULL)))) THEN [UnionAll2].[LoanOutstanding] ELSE [UnionAll2].[LoanOutstanding] END AS [C20],

CASE WHEN (( NOT (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL))) AND ( NOT (([UnionAll2].[C7] = 1) AND ([UnionAll2].[C7] ISNOT NULL)))) THEN CAST(NULL AS decimal(18,2)) WHEN (([UnionAll1].[C5] = 1) AND ([UnionAll1].[C5] IS NOT NULL) AND ( NOT(([UnionAll2].[C6] = 1) AND ([UnionAll2].[C6] IS NOT NULL)))) THEN CAST(NULL AS decimal(18,2)) WHEN (([UnionAll2].[C6] = 1) AND([UnionAll2].[C6] IS NOT NULL))

Modeling your domain models in UML

In any given system under development, the business domain of that system is key. It holds all concepts important to the domain, and captures the business logic from the domain. An important question to answer is what to model about the objects in that domain and how to model this in UML class diagrams.

Identifying the elements of the business domain

First of all, it is important to underline which types of classes participate in the model, simply referred to as the domain model. Aligned with the paradigm of domain driven design, I recommend to model the following types of classes:

  • Domain objects.
  • Factories.
  • Repositories.
  • Services.

Domain objects

A domain object captures a concept from the real world of the application. Think of Contact, Contract, Company or Address. Domain object are considered to have identity. Next, domain objects have properties, and these properties have types. Also, domain object deliver services (often implemented as methods).

image 
A number of domain objects in a class diagram

A concept in the business domain should be considered as such if it has either state (modeled in properties) or delivers services (modeled as operations). Or of course both.

Factories

Factory classes are used to create new instances of domain objects. Although this can often be achieved by just directly creating a domain object from scratch, in most domains creating new object is more complex, especially with the core concepts in the business domain. For instance, when we created a new Contact, it need to have a least one Address which also needs to be created, and it should be aligned with the current Company. When creating such a construct (often referred to as a aggregate in domain driven design) a factory class is used (and modeled) to holds this creation functionality.

A factory class can cover multiple creation scenarios. Name your factory classes after the domain object it creates. Think of classes such as ContactFactory and CompanyFactory. Model each of these scenarios as a method on the factory class.

Repositories

Additionally to the factory classes, a business domain can have repositories. A repository is responsible for answering enquiries about existing instances of domain objects. For instance, you might want to grab all instances of Contact who live in Amsterdam. Name such repositories after the corresponding domain object. Think of ContactRepository or AddressRepository. Model such enquiries as methods on the repository associated with the domain object. Please note that many projects choose to combine the factory and repository classes.

Services

Some services in the business domain do not directly correspond to a domain object, but rather apply to several different domain objects. In this case, such as service can be modeled as a separate class, usually with the name of the actual task as its class name. Think of a service such as  ImportFromExcel that imports a project, its work items and their estimates into a dashboard.

As an alternative, projects may choose not to model separate factories, repositories and services, but rather add all their functionality as services on the actual domain object. In this case, I would recommend stereotyping these methods for example as factory method, repository method or service, so their purpose is immediately clear.

Please note that even though now these methods are modeled on the domain object, they will likely not be implemented there, but on the actual factories and repositories. This alternative is merely a technique to avoid cluttering the domain model with unnecessary classes. Domain models are full enough without adding factories and repositories.

Identifying properties

Now let’s delve down to the domain objects, clearly the most important type of class in the domain model. Next to delivering services, domain objects have properties, such as Name, Email, City, Country, Level.

image
A domain object Contact with a number of public properties

You will need to identify all meaningful properties on your domain objects. Basically you will need to identify all properties that a domain object exposes to the outside world, either other classes in the domain, user interface elements that bind to it, use cases or persistency layers. These are the public properties of the domain object.

Do not model fields that are internal to a domain object as these are not relevant to the business domain but in most cases are only relevant to the developers. Thinks for instance of the Id of a domain object, that is required to store it in a database or to a web service.

A special reminder goes out to properties that are not persisted but are calculated or assembled by the other properties and fields of the domain object. These are called derived properties, and should be marked as such in the domain model.

Modeling property types

And each of these properties has a type. These types actually hold quite a lot of information on the business domain – maybe even more than you’d expect.

I consider the following types of property types:

  • Basic types such as string, DateTime, float.
  • Values types or objects such as ISBN, Email or BSN.
  • Enumerations
  • Variable lists.
  • Associations.

Basic types

Many properties will appear to have a basic type, such as number, string, date, timestamp, float. However, note that for a whole lot of properties, you actually quite a lot more than that it’s just a string. Therefore, for each property check properly if you know more about it than that it just has a basic type.

Value types or object

A value type or object is an object that is recognizable by its value, but where we are not interested in it’s identity. For instance, two instances of my email address will appear to be the same, while two instances of Company that can have exactly the same values for all properties can still be two different companies.

Enumerations

Sometimes, a property can only have a limited set of values. In this case you might consider modeling an enumeration (which is a standard stereotype in UML). This enumeration holds the possible values, so next you can model the property of the type of the enumeration.

image
Enumerations ContactType, PrivacyLevel and Gender.

For instance, a domain object Contact might have a Type property that be Individual, Group or Family. Now model an enumeration ContactType that holds these values, and model the property Type of type ContactType.

But, there’s a restriction to using enumerations. Only use these if you are certain that the list of possible values is static, that is does not change over the course of the life of you application. If you need to add to or change the list of values, this results in re-testing your whole application as the values are often used to perform conditional operations. So if you think the list of values is going to change don’t use an enumeration. Model a variable list instead.

Variable lists

If a property can only have a value from limited set of values, but that list is subject of change during the life of your application, enumeration are not the best solution. The list of values itself should be maintained – either by some software, possibly external to your application, or directly in the database. Think of lists of countries, states or academic titles.

Therefore you will need to take extra care of these properties. Now there’s a number of options here. If the list only uses standard properties (think of Name and Description) and is just used for reference, you might want to group all these different list into one single domain object (and hence table). In this case, an additional properties is required for this domain object, for instance called Type, that describes the type of element, such as Country, State, or AcademicTitle. In this option you will only need a single maintenance use case to maintain all values in all lists. The value or now available for reference, selection or printing on envelopes. We usually refer to this pattern as Smart Reference.

For modeling a property of the type of such a variable list there are a number of options. Either refer to the type of list in the name of the property (call it Country, State or AcademicTitle) and model SmartReference as the type of the property. This works out fine as long as you don’t have to properties of a domain object referring to the same variable list.

A more agreeable option is to give the property the name you want it to have, and model the type of the variable list as the type of the property. To make sure the property is recognized as a variable list, add a stereotype (for instance smartreference) tot the property.

Associations

And then there’s the option that would want to refer another domain object. In this case you normally model an association rather than model a property. An association models a structural relationship between two (or more, but don’t go that road) objects in your business domain. So if a contact has zero, one or multiple relationship, you can model this as a one-to-many association between the domain object Contact and Relationship in your domain model.

image
Two (named) associations between Contact and Relationship.

In code all associations (as in UML) are represented by properties that exist on both classes. So in the example Contact will have a property that represents a list of relationships. And vice versa, each Relationship will have a property of type Contact. Unless you add additional names to the ends of the association in the domain model, the names for these properties are undefined. However it’s a good habit to name them after the domain object they come from. So the properties will simply be called Contact.Relationships and Relationship.Contact – mind the plural.

However, if there’s two different associations between the same domain objects, applying this default naming patterns isn’t enough. It would result in multiple properties with the same name. In that case always specify meaningful names explicitly to the ends of the association. For instance if another association (one-to-one) between Contact and Relationship exists that identifies who’s the from and who’s to to in the relationship, this could result in properties Contact.Froms (a list of type Relationship) and something like Contact.Tos (also a list of type Relatioship).

Another reason for specifying meaningful names to association ends is that these simply occur in the vocabulary of the business domain.

Adding validations

The next step is to add validations to you domain model, and your domain objects in particular. I’ll look at the following types of validations:

  • Are properties mandatory or not?
  • Is the property valid if the type is a value object?
  • Is the property correctly formatted?
  • Does the domain object adhere to the defined business rules?

Mandatory properties

Most properties on domain objects will be mandatory (although debatable). Therefore by default the multiplicity for a property is set to [1..1], which means that there is to be exactly one occurrence for this properties on each object. If a property is not mandatory for the domain object to be valid the multiplicity of such a property is set to [0..1] – which implies that zero or one instances of this property are present.

image
Mandatory and non-mandatory properties

In the example above, BirthName is not mandatory, but AlternativeEmail2 is.

Value objects

The use of value types of objects in your domain objects explicitly adds validation to your objects. It means that the validations rules for this particular value object will apply to such a property automatically. If a property is defined using type Email, all values (when checked) need to follow the rules defined on the value object Email that define when something is in fact a valid Email. Of course, these validations can best be modeled on the value object itself.

image
Properties that have a value object as their type (Email)

In code, this could results in the following property for class Contact (using the ADF.NET framework).

   1: public Email AlternativeEmail2
   2: {
   3:     get { return state.GetValue<Email>(ContactDescriber.AlternativeEmail2); }
   4:     set { state.SetValue(ContactDescriber.AlternativeEmail2, value); }
   5: }

Formatting

Some properties follow a certain format. Think of properties representing money or dates. If the type of the property is a value object, the format should be specified there (often as a regular expression). If not, model the format with the property. Please note that formatting is often culture specific.

Formats can include aspects such as length attributes, for instance a string may not be longer than 64 characters, or must be within a certain range, for instance when a value must be larger than 0 but smaller than 128.

Business rules

Additionally domain objects can validate business rules. For instance on a domain object Relationship, a business rule may validate that both contacts in the relationship match some relationship type. This can be defined in an operation (or a method in code) called ToContactMustMatchRelationshipType. Although this seems like an abundant long name, in fact I recommend to name business rules exactly to what they’re actually doing. Moreover, I recommend to implement the business rules in code using exactly the same name, thus creating traceability.

image 
Modeling business rules in your domain object using the business rule stereotype

I also recommend to add the stereotype business rule to these operations to distinguish them from other methods your classes have. In code the business rule ToContactMustMatchRelationshipType could look something like this. Here the attribute BusinessRule is attached to the method for the same reason, and to allow for automatic validation.

   1: [BusinessRule]
   2: public ValidationResult ToContactMustMatchRelationshipType()
   3: {
   4:     return !To.IsEmpty && RelationshipType.To != To.ContactType
   5:         ? ValidationResult.CreateError(this.Prop(r => r.To), "Contact type '{1}' for {0} does not match required contact type '{2}'.", To, To.ContactType, RelationshipType.To) 
   6:         : ValidationResult.Success;
   7: }

 

An interesting type of business rule is that where the domain object at hand is validated against other domain object (mostly of the same type). For instance, when adding a new OrderType object, even though it is by definition unique (as domain object have identity by nature, usually implemented using an Id property), may not have the same name as any of the other already existing order types. Or a new contract

When to validate?

An intriguing question that comes to mind when discussing validation is when to validate the validations? In a traditional Windows user interface one might say that the moment the user leaves a field and moves to the next the validation for the represented domain object property could or should be fired. In a web application typically the user types in all the fields and then presses submit.

Validating the objects at hand should fire all validations, value objects, mandatory properties, formatting, business rules that are valid for the event (such as save, remove). In the code example from use case Manage Address an Address object is validated and than saved .

   1: public void SaveAddress()
   2: {
   3:     
   4:     if (this.Execute(
   5:         () => Address.Validate(),
   6:         () => Address.Save(),
   7:         () => SaveContact()))
   8:     {
   9:         OK();
  10:     }
  11: }

How Smart Use Cases Can Drive Web Development. Video for session at DevDays 2011 [in Dutch]

as the Channel 9 website says: using real-life code examples Sander will demonstrate how to model, generate and build smart use cases and introduce the positive impact smart use cases have on your layered software architecture.

Anyway, here’s the video for my DevDays 2011 session:

A book on pragmatic software architecture, patterns and frameworks?

One of the major items on my wish list – that is on the professional half of it – is to write a book that displays my ideas on software architecture, patterns and frameworks. Yes I know, there are many books on software architecture, and there are many books that explain patterns, and yes there also are a lot of books that show you how to work with framework A, B or C.

So you would say, why on earth would you want to write a book on these subjects? Well there’s three major reasons. First, there’s not a lot of books out there that combine all these subjects into one single coherent story that will actually help you to write your applications better. Second, maybe I’m a bit arrogant on this, but I feel like I have something to all the stuff that’s out there. And last but not least, I finally need to get this stuff out of my head! Maybe ambitious, but let´s see!

Order from the menu

To be honest, you might like or not like all of the ideas in this book, or even may totally disagree with some of them. Actually, you probably will. But on the whole, I hope this book will be able to inspire you and illuminate you in how to build software. It´s often a matter of taste. So consider this book as a menu. Just order from it what you like. You don´t need to copy the C# and .NET code examples from the book, but rather I hope that use can use them to shape your own ideas on software architecture, frameworks and patterns. Take from it what you like, and forget about the few things you don’t like. 

A first step

What’s the first step towards writing this book? I’ve been teaching about software architecture, patterns and frameworks for .NET for quite a number of years, and the material from these courses will be a good start, similar to the way I shaped my book on UML – from iterating over the material I had for my UML courses.

PIC091019003 

Along with the course material comes the work I’ve done in projects, and as a result the work we (the core team) do on our ever-evolving and improving ADF.NET framework.

Architecture and frameworks

So here’s the overview of the material in the software architecture and patterns course:

  • Introduction. All the usual bla bla and thanks you stuff.
  • How to set up your software architecture? A mild and brief introduction. Thinking about layers, elements in layers,  collaborations (yes CRC). And of cause you can not start without summing up some of the good practices, such as
  • (Software architecture != List<Framework>). How people mix up architecture and frameworks.
  • How frameworks can kill your project. It’s all about dependencies guys. This chapter will be a write out of my talk with the same name.
  • Some patterns to help avoid getting killed. Explaining the different variations of layer super types, my fine descriptor pattern, and the use of dependency injection and the manager-provider pattern.
  • Glue 1.0. My first framework. Short introduction into setting up a minimal framework, using your architecture and the above patterns, minimizing dependencies between your application code and the frameworks you use. It should provide you with the glue to keep your “regular” developers in the loop.
  • Extension methods and fluent interfaces. Have got a bit (not all by far!) to say on how to use them to enable your application developers to code easier and more natural using your first framework.
  • Lasagna and ingredients. What layers would you need in your application? Suggesting presentation, process, domain and data/services. And suggesting a lot of elements in the different layers.

Presentation

In the next chapters I will slowly go through the individual layers. First, let’s investigate the presentation layer:

  • Panels and lists. The basic patterns of presentation layer elements. And a straightforward implementation of a user interface independent panel. Just for the fun of it.
  • The master of detail. Introducing some typical user interface patterns, such as manage, select, search, master-detail, list-detail and the effect of those patterns on your architectural and Glue 1.0 framework requirements.
  • MVWTF. Looking at the different variations in model-view patterns, and discussing the relevance. But briefly, there’s enough said on this stuff.
  • Binding and persisting. Coupling and de-coupling your models, view-models, domain objects, whatever to and from the user interface.
  • The view-model. Sense and non-sense of view-models.
  • Validations. Things to validate and things not to validate in the user interface.

Process

Next the process layer gets a rub:

  • Why do I need a process layer? Explaining the least well-understood layer in application architectures.
  • An brief introduction to smart use cases. Still an excellent unit of work, implementation and testing for projects. This chapter will follow the lines of my presentation on this particular topic.
  • Implementing smart use cases. Introducing the task pattern, and re-iterating the manager-provider pattern. Great stuff I think! Also re-visiting the MVWTF patterns again. 
  • Unit and regression testing smart use cases. How you can write your own extensions to regular unit test framework to allow regression tests over your smart use cases.

Domain

The layer it all revolves about:

  • About being domain driven. Design, code and test, do it domain driven. Now I’m not going to repeat Eric Evans’ book here, but I do repeat (briefly) the basic concepts that derive from his work. Think of entities, repositories, factories, value objects and services.
  • Augmenting your domain. However, we are using some additional patterns in our projects that are useful patterns. Here I’ll discuss the smart reference pattern, revisit value objects, and talk about cached domain objects. I might even introduce you to search objects.
  • Business rules and validations. How do you write and where do you place your business rules, and what validations do you execute in the domain layer?
  • Independent state. Domain objects, or entities, have identity and state. This state can come from a lot of place – not all being SQL Server. So the big question here is: can I keep my domain objects independent of how we get and set its state. Well yes you can, hence the internal state patterns jumps in.

Data, services and the outside world

All the way down to the bottom, in the cellar of your application, lies the data / services layer:

  • Does the data layer really exist? Hey weren’t we using NHibernate or Entity Framework? So why do we need a data layer, and if so, what is in it?
  • Map and wrap. When querying data sources it is very likely that you will need to provide some sort of mapping from your world to the outside world. I’ll show you some simple patterns of to achieve this mapping, allowing you to wrap your domain objects around the outside world’s objects.
  • Creating queries. Again, do you really need to implement a query patterns. We do have LINQ do we? Well yes, but LINQ does not cover all your possible sources of data – a lot, but not all. So a brief introduction of the query pattern – and some implementation aspects might not be a bad idea.
  • On the way out. Somehow you will need to reach out to the outside world of your application. Virtually the outside world can be just about anything. A (set of) databases of course, but what about OData, JSON, plain or not so plain XML, restful or restless web services, Twitter, Entity Framework, NHibernate, Facebook, you name it. The gateway pattern should help you here.

Concluding

    Mildly put, this little blog post could well be the introduction for this new book – except for the thank you notes. Do you miss anything, or already agree or disagree? Want to help annotating or reviewing the manuscript in progress? Don´t hesitate to become a part of the writing process. Please comment here!

Please vote for my Microsoft Mix 2011 proposals!

From April 12-14 the next edition of Microsoft’s MIX Conference will take place in Las Vegas. I’ve sent in two proposals for the Open Call. Today heard that both proposals made it through the first cut, which means they’re open for public voting (you don’t have to be registered). It would be great if you would cast your vote for my proposals on how smart use case can drive web development, and on how frameworks can kill your projects.

300x250_Mix11_011011_US_b

Proposed talk 1: How smart use cases can drive ASP.NET web development

Use cases have been around for many years describing the requirements of software development projects. By developers, use cases are often seen as too abstract and too complex to develop code from. That is, until now.

During this interactive talk speaker Sander Hoogendoorn will demonstrate how to model, generate and build smart use cases. This great technique allows projects to model use cases at a much more pragmatic, and low-granular level, that can be implemented straightforward and directly into our applications, such as ASP.NET or Silverlight web application. The speaker will introduce both the positive impact smart use cases have on your layered software architecture, as well as the design patterns required to implement smart use cases. This talk comes with many real-life code examples.

Proposed talk 2: How frameworks can kill your projects

When it comes to .Net development, more and more frameworks enter the market. Both from Microsoft and from open source. think of ASP.NET MVC, Castle, WF, Entity Framework, Unity, Linq2SQL, ADO.NET Data Services, WCF, nHibernate, Spring.NET, CSLA, NUnit, Enterprise Library or ADF.

Once a project chooses to apply one or more frameworks, trouble begins. What if you require features that aren’t implemented in the framework? What if you decide that another framework would have been better and want to switch halfway your project? What if the author of your favorite open source framework suddenly stops developing? What if the framework contains bugs or omissions? And what if a new version of the framework is released that is implemented totally different? These and many more everyday problems will cause your project to come to a halt, or at least make you perform serious refactoring.

During this highly interactive talk Sander Hoogendoorn, chief architect of Capgemini?s agile Accelerated Delivery Platform, and member of Microsoft?s Partner Advisory Council .NET, demonstrates pragmatic architectures and patterns that will help your projects to stay away from framework issues, and how to keep code independent of framework choices. Sander will present models of layered architectures, and apply bridge patterns, managers-providers, dependency injection, descriptors, and layer super-types.

Of course, the speaker will illustrate these insightful patterns with lots of demo?s and (bad) code examples using blocks from Microsoft?s Enterprise Library, NHibernate, Log4Net, and the Entity Framework. Delegates benefit by learning how to improve the structure and quality of their software architecture and code, and how to avoid the pitfalls of applying frameworks to .Net software development.

Simple little things. Issue commands using CommandName and CommandArgument in a GridView control

Handling events in ASP.NET is not always as straightforward as it seems. One particular pattern that often occurs is that there’s several items in a row in a GridView control that trigger commands. Now the GridView supports several standard commands such as Select and Delete, but what if you want to issue your own command?

As an example, here’s my situation. I have a GridView that displays relationships between contacts, such as in the picture below.

Here, the left part triggers another use case to update the relationship, but the second part needs to start a use case to view the other contact in the relationship. How did I implement this?

image

First of all, you will need to add a DataCommand to the control (in my case a LinkButton. Here I specified it with “OtherContact”.

   1: CommandName="OtherContact" 

When you now click on the LinkButton you will be able to capture this command from the event arguments of the RowCommand event, which fires upon clicking the button. So first you will need to add a handler to the event.

   1: this.grdRelationship.RowCommand += new GridViewCommandEventHandler(grdRelationship_RowCommand);

Using CommandName

The RowCommand event comes with event arguments, which is an instance of the GridViewCommandEventArgs class. As you might expect this class has a property CommandName you will be able to verify.

Now for the somewhat harder part. My GridView is bound to a collection of Relationship objects, and I need to find out which of the rows is actually clicked to be able to pass the right Relationship to the next use case. Hence, I need to get hold of the index of the row that was clicked. Unfortunately, this index is not a valid property of the GridViewCommandEventArgs, nor on any of its properties properties.

Using CommandArgument

However, there is an additional property available that might help, and that is the CommandArgument property. Just like the CommandName this property can be set on the LinkButton in the GridView. The trick here is to set it (for example) to the index of the row it is in. I’ve used the following expression to achieve this.

   1: CommandArgument='<%# DataBinder.Eval(Container, "RowIndex")%>'  

Of course there’s alternatives such as putting in the Id for the actual Relationship object, but RowIndex seems fine to me – this way the object’s identification at least doesn’t show up in HTML.

Putting it together

The last step here is to put it all together in handling the RowCommand event. The remaining code isn’t hard to understand.

   1: void grdRelationship_RowCommand(object sender, GridViewCommandEventArgs e)
   2: {
   3:     if (e.CommandName == "OtherContact")
   4:     {
   5:         Relationship r = Relationships[Convert.ToInt32(e.CommandArgument)];
   6:  
   7:         MyTask.ShowOtherContact(r);
   8:     }

I simply validate the CommandName and use the CommandArgument to identify the relationship that was clicked as pass that relationship to the underlying use case (in the MyTask property of the page).

November 12, 2010 – Microsoft TechEd Europe. How smart use cases can drive web development

[Session ARC205 at Microsoft TechEd Europe 2010 in Berlin]

Use cases have been around for many years describing the requirements of software development projects. From a developer’s point of view, use cases are often seen as too abstract and too complex to develop code from. Until now, that is.

IMAGE_004[5]

During this interactive talk, speaker Sander Hoogendoorn will demonstrate how to model, generate and build smart use cases.

image[8]_thumb[2]

This great technique allows you to model use cases at a much more pragmatic, low-granular level, enabling them to be implemented simply and directly into applications such as ASP.NET or Silverlight. Using many real-life code examples, the speaker will introduce both the positive impact that smart use cases have on your layered software architecture, as well as the design patterns required to implement them.

Simple little things. The 42 extension method anti-pattern

Extension methods are a powerful feature in .NET, and have been increasingly adopted by the developer community. Also, LINQ and other framework features rely heavily on extension methods. In fact, extension methods were originally invented to be able to implement LINQ without having to change a lot of .NET framework base classes.

Basically, an extension method is implemented as a static method on a static class. For those of you who have never seen an extension method in real life, the first parameter in the signature of an extension method might look a bit peculiar.

public static bool Execute(this Task task, params Action[] actions)
{
    foreach (var action in actions)
    {
        action.Invoke();
 
        if (!ValidationManager.IsSucceeded)
        {
            ValidationManager.Handle();
            return false;
        }
    }
 
    return true;
}

This first parameter typed with the keyword this is just syntactic sugar to be able to tell for which type the extension method is valid. In this example it is valid for any instances of the type Task, and all of its subtypes.

Like using a regular method?

Using the extension method seems exactly like using a regular method on such instances, as in the example below.

public void RemoveTag()
{
    if (this.Execute(() => Tag.Remove()))
    {
        TaskManager.Run(this, select);
    }
}

But there’s a slight difference. Since the extension method is not implemented on the type itself, it is unable to reference the private parts of the type. Moreover, the extension method can only be called only on instances of the specific type, and not statically on the type itself.

Object extension methods

So far, so good. Now, as I said earlier, the developer community has warm embraced this cool .NET feature. In fact, when you browse for extension method on the internet, there’s tons of useful – and useless – extension methods available. If you look really careful, most of these are either defined as valid for object of string.

This is where the 42 anti-pattern comes from. Sometimes it’s hard to define the best location for an extension method. In the example of the Execute() methode above we couldn’t make up our minds – should it be valid for Task or for DomainObject?

In such case I can imagine that there will be developers out there who will now define the method on object, allowing the extension method to be applied from any arbitrary object in the application, clearly not what extension methods are intended.

public static bool Execute(this object task, params Action[] actions)

42

And this is where things are starting to dive of the deep end. Not only will you end up with a whole bunch of method that have nothing to do with most objects in your application, it can get even worse when you don’t have an object in your application that I can use to call the extension method on. What to do now?

Well, what if you would just define an arbitruary object hosting the extension method. Good idea dudes! We’ll just define the int instance 42 – just to have some fun and the method on 42.

if (42.Execute(() => Product.Remove()))
 {
     TaskManager.Run(this, select);
 }

Isn’t that sweet? I’m curious to see how long it takes until we find such code.