Wednesday, May 29, 2013

Limited .DefaultIfEmpty support is available on NHibernate now

As of the time of this writing, DefaultIfEmpty is not yet available on NHibernate from NuGet, so just get the latest NHibernate directly from GitHub instead, then compile it.

When we need to do this kind of SQL, we have to use Linq's DefaultIfEmpty.
select
    b.blog_id,
    b.blog_title,
    p.blog_text
from      blog b
left join blog_post p on b.blog_id = p.blog_id


DefaultIfEmpty is supported now on NHibernate, it works on collections now:
var results = 
    from b in s.Query<Blog> ()
    from p in b.Posts.DefaultIfEmpty () 
    select new { b.BlogId, b.BlogTitle, BlogText = p.BlogText };


foreach (var item in results) {
    Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}


Whereas prior to DefaultIfEmpty support, it's hair-splitting to write the LEFT JOIN query in QueryOver API, it's a bit convoluted to use QueryOver, especially for those who are at ease with Linq and SQL. This is the adhoc-y approach, i.e. using anonymous type, good to use when you wanted to immediately return a JSON object:
BlogPost bpAlias = null;
var results = s.QueryOver<Blog> ()
    .Left.JoinAlias (b => b.Posts, () => bpAlias)
    .SelectList (z =>  z.Select(b => b.BlogId).Select(b => b.BlogTitle).Select(b => bpAlias.BlogText))
    .List<object[]> ()
    .Select (z => new { BlogId = (int)z[0], BlogTitle = (string)z[1], BlogText = (string)z[2] });


foreach (var item in results) {
    Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}


This is the DTO approach. All hell break loose if you want to use a DTO on your QueryOver ツ
public class BlogPlusPostDto
{

    public int BlogId { get; set; }
    public string BlogTitle { get; set; }
    public string BlogText { get; set; }
}


.
.
.

BlogPost bpAlias = null;

BlogPlusPostDto bpDto = null;

var results = s.QueryOver<Blog> ()
    .Left.JoinAlias (b => b.Posts, () => bpAlias)
    
    .SelectList (z =>  z
                        .Select(b => b.BlogId).WithAlias(() => bpDto.BlogId)
                        .Select(b => b.BlogTitle).WithAlias(() => bpDto.BlogTitle)
                        .Select(b => bpAlias.BlogText).WithAlias(() => bpDto.BlogText)
                 )
    .TransformUsing (Transformers.AliasToBean<BlogPlusPostDto>())
    .List<BlogPlusPostDto> ();


foreach (var item in results) {
    Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}

Contrast that on using DTO with Linq. The Linq version is much shorter, and without the noise of WithAlias and Transformers that gets in the way of your query's beautiful objective. Plus it's easy to mock and unit test in-memory objects with IQueryable:
public class BlogPlusPostDto
{

    public int BlogId { get; set; }
    public string BlogTitle { get; set; }
    public string BlogText { get; set; }
}


.
.
.

var results = 
 (from b in s.Query<Blog> ()
 from p in b.Posts.DefaultIfEmpty () 
 select new { b.BlogId, b.BlogTitle, BlogText = p.BlogText }).ToList ()
  .Select (x => new BlogPlusPostDto { BlogId = x.BlogId, BlogTitle = x.BlogTitle, BlogText = x.BlogText });
 


foreach (var item in results) {
 Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}


If lambda is your thing, you can convert the Linq code to the equivalent lambda code, also working on latest NHibernate:
var xx = s.Query<Blog>()
     .SelectMany (blog => blog.Posts.DefaultIfEmpty()
                           .Select(post => new { blog.BlogId, blog.BlogTitle, BlogText = post.BlogText })
     );

foreach(var item in xx) {
     Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}


Now, about the limited part, we still cannot use DefaultIfEmpty on manual join operation, this results to runtime error that says the requested feature is not implemented.
var results = 
     from b in s.Query<Blog>()
     join p in s.Query<BlogPost>() on b equals p.Blog into bJoinP

     from r in bJoinP.DefaultIfEmpty()
     select new { b.BlogId, b.BlogTitle, r.BlogText };

foreach(var item in results) {
    Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}


And DefaultIfEmpty on NHibernate Linq's GroupBy has a bug on Count, NHibernate's Count cannot do conditional counting even if you passed a condition on it, it always blindly do COUNT(*), which always results to at least a count of 1, never having a count of zero. LEFT JOIN should be able to report aggregated count of zero.


To do a working COUNT with GROUP BY on NHibernate: http://www.ienablemuch.com/2012/12/nhibernates-non-stringly-typed-left-join.html


Happy Computing! ツ

Friday, May 24, 2013

Automating Integration Test with WatiN and Microsoft Test

A physicist is an atom's way of knowing about atoms. – George Wald



We also have that in software development field. The web developers are the way of the browsers to test itself.



Having said that, we should not be subjected to test the application correctness manually. We can also automate this kind of stuff



To cut to the chase, I used WatiN to test the application automatically. Automating integration test is also good for regression testing



It's very easy to use WatiN for test automation. Here's a sample test for doing integration test:


using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WatiN.Core;
  
namespace TestTheWatiN.Tests
{
    [TestClass]
    public class TheUnitTest
    {
        public void Logon(Browser browser)
        {
            // Arrange
            var uxUsername = browser.TextField(Find.ByName("LoginFields.Username"));
            var uxPassword = browser.TextField(Find.ByName("LoginFields.Password"));
            var uxLogin = browser.Link(Find.ById("loginButton"));
                 
            // Act
            uxUsername.TypeTextQuickly("mb");
            uxPassword.TypeTextQuickly("opensesame");
            uxLogin.Click();
  
            // Assert
            Assert.IsTrue(browser.ContainsText("Welcome"));
        }
  
  
        [TestMethod]
        [Timeout(10 * 60 * 1000)]
        public void TestSocial()
        {
            string devSite = "http://blah/DEV/Account/Login";
            // string qaSite = "http://blah/QA/Account/Login";
  
            string testSite = devSite;
  
            using (var b = new IE(testSite))
            {
                Logon(b);
                Social_test_if_navigation_is_working_properly(b);
                Social_post_then_check_if_message_is_added(b);
            }
        }
  
        public void Social_test_if_navigation_is_working_properly(Browser browser)
        {
            // Arrange
            var link = browser.Link(Find.BySelector(".sectionContainer a"));
  
            // Act
            link.Click();
  
            // Assert
            Assert.IsTrue(browser.ContainsText("Share"));
        }
  
         
        public void Social_post_then_check_if_message_is_added(Browser browser)
        {
            // Arrange
            const string placeholder = "Write your status...";           
            const string waitBeforeAssert = "Just Now";
  
            string expectedAdded = Guid.NewGuid().ToString() + " are like snowflakes";
  
            var uxTextToPost = browser.TextField(Find.ByName("PostedText"));
            var uxSave = browser.Link(Find.ById("SaveButton"));
  
             
            // Act
            uxTextToPost.Focus();
            uxTextToPost.TypeTextQuickly(expectedAdded);
            uxSave.Click();           
            browser.WaitUntilContainsText(waitBeforeAssert);
             
  
            // Assert           
            Assert.AreEqual(placeholder, uxTextToPost.Text);
            Assert.IsTrue(browser.ContainsText(expectedAdded));
        }
  
    }
  
    public static class WatiNExtensions
    {
        public static void TypeTextQuickly(this TextField textField, string text)
        {
            // Slow:
            // textField.TypeText(text);
  
            // Fast:
            textField.SetAttributeValue("value", text);
        }
    }
}


Happy Computing! ツ

My musings on domain modeling. To map or not to map a collection

There's no such thing as bloated model, especially NHibernate doesn't eagerly load an entities' collections when we don't explicitly tell it so.



To paraphrase:

A bug is just a missed unit/integration test



Using Linq's join and .DefaultIfEmpty combo or using QueryOver's .Left.Join is just a missed collection mapping.



I would rather have the Requirements Analyst read the domain models which reflects that we have a business need for a model to report its collections than let them read the QueryOver or join-using Linq. Another reason why ORM and modeling is invented, so for us to have a common language between developers and Requirements Analysts. We should not let our query be our single source-of-truth to reflect the business needs / model relationships




Domain modelling should reflect the fact that we have a business need for something, not that we developers just have a need for a beautiful facade for the data store. So if there's a business need to report a given model's collection, we shall map it then, and let the domain models reflect that fact. We should not let the QueryOver, Linq,and even SQL be the go-to place to verify a business need.



The only time the collection doesn't need to be mapped is if there's no business needs for it. Let's just map collections on-the-fly, that is when when we need them, that is when the business needs arises. We should not be overzealous with model mappings and merrily map all the collections to their owning model.


To make a concrete example of when not to map a collection to their owning entity is the Person model. It's a given that all models have a CreatedByPerson property that indicates the user who created the record. But we will not blindly map all the models as a collection to Person if there's no business need to report all the created records of a given person. Doing so even there's no need, will just complicate/bloat our domain model.


The guidance is, we should not be overly-worried about bloating the models(we will not bloat things deliberately, conscious software development in the action), but rather we should be overly-worried if the domain models doesn't reflect the business needs.

Wednesday, May 22, 2013

When a language feature renders the naming convention / design pattern obsolete

I initially wrote this kind of code:

string i18n_EmployeeName = I18nUtility.GetGlobalResourceString("EmployeeName");
string i18n_CurrentRecipient = I18nUtility.GetGlobalResourceString("CurrentRecipient");
string i18n_SubmittedBy = I18nUtility.GetGlobalResourceString("SubmittedBy");
string i18n_Submitted = I18nUtility.GetGlobalResourceString("Submitted");
string i18n_EffectiveDate = I18nUtility.GetGlobalResourceString("EffectiveDate");
string i18n_Reason = I18nUtility.GetGlobalResourceString("Reason");
string i18n_OffCycleApprovalStatus = I18nUtility.GetGlobalResourceString("Status");
 
string dateFormat = "m/dd/yyyy";
 
.
.
.
 
byte[] ret = ExcelResultFromList.Create(
    mapAction: map =>
    {
       map.Include(x => x.EmployeeName).Label(i18n_EmployeeName);
       map.Include(x => x.CurrentRecipient).Label(i18n_CurrentRecipient);
       map.Include(x => x.SubmittedBy).Label(i18n_SubmittedBy);
       map.Include(x => x.Submitted).Label(i18n_Submitted).Format(dateFormat);
       map.Include(x => x.EffectiveDate).Label(i18n_EffectiveDate).Format(dateFormat);
       map.Include(x => x.ReasonCode).Label(i18n_Reason);
       map.Include(x => x.OffCycleApprovalStatus).Label(i18n_OffCycleApprovalStatus);
    },
    mapActionForSecond: map =>
    {
        map.Include(x => x.EmployeeName).Label(i18n_EmployeeName);
        map.Include(x => x.CurrentRecipient).Label(i18n_CurrentRecipient);
        map.Include(x => x.SubmittedBy).Label(i18n_SubmittedBy);
        map.Include(x => x.Submitted).Label(i18n_Submitted).Format(dateFormat);
        map.Include(x => x.EffectiveDate).Label(i18n_EffectiveDate).Format(dateFormat);
        map.Include(x => x.ReasonCode).Label(i18n_Reason);
        map.Include(x => x.OffCycleApprovalStatus).Label(i18n_OffCycleApprovalStatus);
    }
)

I don't want to create a separate class just to emphasize the related variables (e.g. labels), it's too ceremonial. I put prefix on those label variables so I can segregate them from business variables(i.e. variables use for computation, flow, etc), achieving separation of concerns via naming convention. Whether prefixing things is desirable or not is entirely another matter; but certainly, separation of concerns must be addressed.


And then I realize, I can do adhoc-y structure with anonymous type, and voila! naming convention is not necessary anymore to achieve separation of concerns. And suddenly, naming convention feels so WET


var i18n = new
               {
                   EmployeeName = I18nUtility.GetGlobalResourceString("EmployeeName"),
                   CurrentRecipient = I18nUtility.GetGlobalResourceString("CurrentRecipient"),
                   SubmittedBy = I18nUtility.GetGlobalResourceString("SubmittedBy"),
                   Submitted = I18nUtility.GetGlobalResourceString("Submitted"),
                   EffectiveDate = I18nUtility.GetGlobalResourceString("EffectiveDate"),
                   Reason = I18nUtility.GetGlobalResourceString("Reason"),
                   OffcycleApprovalStatus = I18nUtility.GetGlobalResourceString("Status")
               };
 
string dateFormat = "m/dd/yyyy";
 
.
.
.
 
byte[] ret = ExcelResultFromList.Create(
    mapAction: map =>
    {
       map.Include(x => x.EmployeeName).Label(i18n.EmployeeName);
       map.Include(x => x.CurrentRecipient).Label(i18n.CurrentRecipient);
       map.Include(x => x.SubmittedBy).Label(i18n.SubmittedBy);
       map.Include(x => x.Submitted).Label(i18n_Submitted).Format(dateFormat);
       map.Include(x => x.EffectiveDate).Label(i18n.EffectiveDate).Format(dateFormat);
       map.Include(x => x.ReasonCode).Label(i18n.Reason);
       map.Include(x => x.OffCycleApprovalStatus).Label(i18n.OffCycleApprovalStatus);
    },
    mapActionForSecond: map =>
    {
        map.Include(x => x.EmployeeName).Label(i18n.EmployeeName);
        map.Include(x => x.CurrentRecipient).Label(i18n.CurrentRecipient);
        map.Include(x => x.SubmittedBy).Label(i18n.SubmittedBy);
        map.Include(x => x.Submitted).Label(i18n.Submitted).Format(dateFormat);
        map.Include(x => x.EffectiveDate).Label(i18n.EffectiveDate).Format(dateFormat);
        map.Include(x => x.ReasonCode).Label(i18n.Reason);
        map.Include(x => x.OffCycleApprovalStatus).Label(i18n.OffCycleApprovalStatus);
    }
)


Happy Computing! ツ

Thursday, May 16, 2013

.NET The Future

Windows/Azure has a bright future and .NET does not. Learn as many as platforms as you needed for your career, it's not on Microsoft's favor to push only .NET to developers when they can earn billions if they support all developer tools, regardless if it come from them or not. Developer's tools from across all platforms, be it programming language, database, and heck even an OS, are all given equal treatment with Azure, it's not partial to .NET nor Windows only.


And if in 1999, the necessary technology and infrastructure for building clouds(e.g. Azure, Amazon, Heroku, etc) were already available then and ready for prime time, Microsoft doesn't even need to create a better Java (read: .NET) to earn billions and developers mindshare. That's a food for thought.



http://odetocode.com/blogs/scott/archive/2013/05/15/where-is-net-headed.aspx

Saturday, May 11, 2013

ASP.NET MVC IDE-friendly URL

Instead of using hardcoded url or Url.Content...

var theUrl = '@Url.Content("~/MyPeople/OffCycleCompensation/IsEligible")';        
 
        $.ajax({
            url: theUrl,
            type: 'POST',
            async: false,
            data: { personID : personID },
            success: function (data) {


...use IDE-friendly URL helpers to make maintenance programming easier, e.g. use Url.RouteUrl:


var theUrl = '@Url.RouteUrl("AreaRoute", new { area = "MyPeople", controller= "OffCycleCompensation", action="IsEligible" })';        
 
        $.ajax({
            url: theUrl,
            type: 'POST',
            async: false,
            data: { personID : personID },
            success: function (data) {
 


Advantage being, the code is more navigable, just hold Ctrl and press left click on IsEligible, the IDE will bring you to IsEligible method. If you are using ReSharper, just position the cursor on IsEligible and press Ctrl+B



Little things that works wonders



Happy Coding! ツ