Thursday, January 9, 2014

I love the word The

Want to enforce SchemaName.TableName to your domain models (think AdventureWorks)? i.e., you wanted this:

var personList = 
    from p in session.Query<Person.Person>
    select p;

var storeList = 
    from s in session.Query<Sales.Store>
    select s;


You can't use namespace..

namespace Domain.Models
{
    namespace Person
    {
        public class Person
        {
        }
        public class BusinessEntityContact
        {
        }
    }


    namespace Sales
    {
        public class Store
        {
        }
    }
}


..as developers can opt out of Person or Sales namespace by importing the namespace through C#'s using, some would do this:

using Domain.Models.Sales;


.
.
.


var list = 
    from p in session.Query<Store>
    select p;




To achieve enforcement of Schema name on your domain classes, do this instead:

namespace Domain.Models
{
    public static class Person
    {
        public class Person
        {
        }
    
        public class BusinessEntityContact
        {
        }
    }

    public static class Sales
    {
        public class Store
        {
        }
    }
}

However that will not work, it's not allowed for the nested class to have the same name as its containing class, e.g., Person.Person. So we must use some convention to eliminate the compiler error through naming convention, e.g.:


namespace Domain.Models
{
    public static class PersonSchema
    {
        public class Person
        {
        }
    
        public class BusinessEntityContact
        {
        }
    }

    public static class SalesSchema
    {
        public class Store
        {
        }
    }
}

But I prefer prefixing the word The:


namespace Domain.Models
{
    public static class ThePerson
    {
        public class Person
        {
        }
    
        public class BusinessEntityContact
        {
        }
    }

    public static class TheSales
    {
        public class Store
        {
        }
    }
}


Using that convention, reading the code rolls off the tongue quite nicely:


var personList = 
    from p in session.Query<ThePerson.Person>
    select p;

var businessContactList = 
    from c in session.Query<ThePerson.BusinessContact>
    select c;


var storeList = 
    from s in session.Query<TheSales.Store>
    select s;


Sorry Entity Framework, the Set method doesn't cut it:

var list = 
    from s in context.Set<TheSales.Store>
    select s;


Happy Coding! ツ

Wednesday, January 8, 2014

Partial Class Is A Boon For Code Generator Developers

I got this following exception with NHibernate..

Cannot instantiate abstract class or interface: TestInheritance.DomainModels.BusinessEntity

..with these AdventureWorks2012 domain models:

public abstract class BusinessEntity
{
    public virtual int BusinessEntityID { get; set; }
}

public class Person : BusinessEntity
{
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class Employee : Person
{
    public virtual string NationalIDNumber { get; set; }
    public virtual string JobTitle { get; set; }
}

public class Store : BusinessEntity
{
    public virtual string Name { get; set; }        
}

The code I tried:

var list = s.Query<BusinessEntity>().ToList();


Basically, I just want to get all business entities. Upon reading this..

After suffering this error message for a while the reason turned out to be almost logical: In an @Inheritance of type Joined, there was an entry in the root table but no entry in any of the inheriting tables. -- Kolov



..it became obvious why mapping only the above domain models produces an error, there's some BusinessEntityID in abstract BusinessEntity that is not in any of the domain models above, the fallback of NHibernate is to instantiate the base class when its ID is not in the inheritance tree, hence resulting to an exception, since abstract classes cannot be instantiated. In fact, we can also make the error go away by making the BusinessEntity domain model (an abstract class) a concrete class, however there's no sense making BusinessEntity a concrete class.


Armed with the above knowledge in mind, I queried which tables are referencing the BusinessEntity domain model:

SELECT  
  ForeignTableSchema = KCU1.TABLE_SCHEMA
  ,ForeignConstraintName = KCU1.CONSTRAINT_NAME
  ,ForeignTableName = KCU1.TABLE_NAME 
  ,ForeignColumnName = KCU1.COLUMN_NAME
  ,ForeignOrdinalPosition = KCU1.ORDINAL_POSITION

  ,ReferencedTableSchema = KCU2.TABLE_SCHEMA
  ,ReferencedConstraintName = KCU2.CONSTRAINT_NAME
  ,ReferencedTableName = KCU2.TABLE_NAME 
  ,ReferencedColumnName = KCU2.COLUMN_NAME
  ,ReferencedOrdinalPosition = KCU2.ORDINAL_POSITION

   
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC 

INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU1 
  ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG  
  AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
  AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 

INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU2 
  ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG  
  AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA 
  AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME 
  AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 

WHERE KCU2.CONSTRAINT_NAME = 'PK_BusinessEntity_BusinessEntityID'
ORDER BY KCU1.TABLE_NAME

Here's the result:






Knowing that I forgot to include the Vendor domain model, I then mapped it, then the exception problem goes away! Just merely looking at the result above, it's not instantly obvious if the BusinessEntityAddress and/or BusinessEntityContact is the table(s) that is also causing the exception above. You can only infer it by looking at their primary key, if their primary key relates one-to-one to BusinessEntity, then those models needed be mapped too. However seeing they are not one-to-one to BusinessEntity, then not mapping them won't cause exception to the abstract BusinessEntity domain model, to wit:




As we can see, they are not an aggregate root, those domain models makes sense only within the domain of an another domain model. This is where things get tricky for the code generator, even if we can indicate that the BusinessEntity is an abstract domain model (hence we can prevent it from becoming the aggregate root to BusinessEntityAddress, BusinessEntityContact or any domain models for that matter), it's impossible for code generator to deduce on which aggregate root the BusinessEntityAddress and BusinessEntityContact belongs to. This is where the code generator needed an intervention from someone with business knowledge of the domain models, these models are meant to be mapped manually, partial class totally empowers this needed manual mapping.



Happy Coding! ツ



Mapping:
public class BusinessEntityMapping : ClassMapping<BusinessEntity>
{
    public BusinessEntityMapping()
    {
        Table("Person.BusinessEntity");
        Id(x => x.BusinessEntityID, m => m.Generator(NHibernate.Mapping.ByCode.Generators.Identity));            

    }
}


public class PersonMapping : JoinedSubclassMapping<Person>
{
    public PersonMapping()
    {

        Table("Person.Person");

        Key(k => k.Column("BusinessEntityID"));

        Property(x => x.FirstName);
        Property(x => x.LastName);
    }
}



public class EmployeeMapping : JoinedSubclassMapping<Employee>
{
    public EmployeeMapping()
    {
        Table("HumanResources.Employee");

        Key(k => k.Column("BusinessEntityID"));


        Property(x => x.NationalIDNumber);
        Property(x => x.JobTitle);
    }
}


public class StoreMapping : JoinedSubclassMapping<Store>
{
    public StoreMapping()
    {
        Table("Sales.Store");

        Key(x => x.Column("BusinessEntityID"));

        Property(x => x.Name);
    }
}


public class VendorMapping : JoinedSubclassMapping<Vendor>
{
    public VendorMapping()
    {
        Table("Purchasing.Vendor");

        Key(x => x.Column("BusinessEntityID"));

        Property(x => x.AccountNumber);
        Property(x => x.Name);
    }
}

Tuesday, January 7, 2014

Polyfill: Web Developer's Version of "There's an app for that"™

Placeholder is not working on IE9 and below. Fortunately there's an app.. er.. "There's a polyfill for that!"™

To use placeholder on IE, just drop this polyfill to your site: http://jamesallardice.github.io/Placeholders.js/


Friday, January 3, 2014

Want to try out some piece of .NET code yet you don't want to accumulate clutters of solutions in your recent projects in Visual Studio?

I saw some answer on stackoverflow I need to try out:

static void Main() {
 
    string s1 = Regex.Replace("abcdefghik", "e",
        match => "*I'm a callback*");
 
    string s2 = Regex.Replace("abcdefghik", "c", Callback);
}
static string Callback(Match match) {
    return "*and so am i*";
}

But I don't want to create a new solution just for that smallish code. .NET fiddle to the rescue!
Example: http://dotnetfiddle.net/FSqLmM

And what's cooler with .NET Fiddle, is that it has auto-complete, unlike ideone

If only I can try snippets of NHibernate, Entity Framework, ASP.NET MVC or SignalR on .NET Fiddle, my folders will be a lot more tidy :D



Happy Coding! ツ