Thursday, December 5, 2013

Let the API do the heavy lifting

Instead of concatenating directory and filename manually:

string dir = @"C:\Windows";
string filename = "notepad.exe";
Console.WriteLine(dir + @"\" + filename);


Use Path.Combine:

string dir = @"C:\Windows";
string filename = "notepad.exe";
Console.WriteLine(Path.Combine(dir, filename));


Output: C:\Windows\notepad.exe


Aside from it take care of the nuances of platform differences(slash for *nix-based systems (e.g. Mono on Linux), backslash for Windows-based), it also takes care of trailing slash/backslash of the directory


The output of the following is still: C:\Windows\notepad.exe

string dir = @"C:\Windows\";
string filename = "notepad.exe";
Console.WriteLine(Path.Combine(dir, filename));


C# on Unix: http://ideone.com/rTACSJ

C# on Windows: http://dotnetfiddle.net/kxVNW5



Happy Coding! ツ

Saturday, November 30, 2013

Typical NHibernate SessionFactory boilerplate

using DomainMapping.Mappings;

using NHibernate.Cfg;


using System.Linq;

using System.Collections.Generic;



namespace DomainMapping
{
    public static class Mapper
    {


        static NHibernate.ISessionFactory _sessionFactory = Mapper.BuildSessionFactory();


        // call this on production
        public static NHibernate.ISessionFactory SessionFactory
        {
            get { return _sessionFactory; }
        }


        // Call this on unit testing, so we can test caching on each test method independently
        public static NHibernate.ISessionFactory BuildSessionFactory(bool useUnitTest = false)
        {
            var mapper = new NHibernate.Mapping.ByCode.ModelMapper();

            mapper.AddMappings
                (
                    typeof(Mapper).Assembly.GetTypes()
                          .Where( x => x.BaseType.IsGenericType
                                       && x.BaseType.GetGenericTypeDefinition()==typeof(NHibernate.Mapping.ByCode.Conformist.ClassMapping<>)) 
                );

            // Or you can manually add the mappings
            // mapper.AddMappings(new[]
            //    {
            //        typeof(PersonMapping)
            //    });


            var cfg = new NHibernate.Cfg.Configuration();

            // .DatabaseIntegration! Y U EXTENSION METHOD?
            cfg.DataBaseIntegration(c =>
            {
                var cs = System.Configuration.ConfigurationManager.ConnectionStrings["TheJunBetConnection"].ConnectionString;

                // SQL Server
                c.Driver<NHibernate.Driver.SqlClientDriver>();
                c.Dialect<NHibernate.Dialect.MsSql2008Dialect>();
                c.ConnectionString = "Server=localhost;Database=TestTheDatabase;Trusted_Connection=True;MultipleActiveResultSets=True";

                // // PostgreSQL
                // c.Driver<NHibernate.Driver.NpgsqlDriver>();
                // c.Dialect<NHibernate.Dialect.PostgreSQLDialect>();
                // c.ConnectionString = "Server=localhost; Database=test_the_database; User=postgres; password=opensesame";                


                if (useUnitTest)
                {
                    c.LogSqlInConsole = true;
                    c.LogFormattedSql = true;
                }
            });

            var domainMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();


            // // AsString is an extension method from NHibernate.Mapping.ByCode:
            // string mappingXml = domainMapping.AsString();

            // // Life without Resharper. 
            // string mappingXml = NHibernate.Mapping.ByCode.MappingsExtensions.AsString(domainMapping);

            // System.Console.WriteLine(mappingXml);


            cfg.AddMapping(domainMapping);


            // http://www.ienablemuch.com/2013/06/multilingual-and-caching-on-nhibernate.html
            //var filterDef = new NHibernate.Engine.FilterDefinition("lf", /*default condition*/ null,
            //                                                       new Dictionary<string, NHibernate.Type.IType>
            //                                                           {
            //                                                               { "LanguageCultureCode", NHibernate.NHibernateUtil.String}
            //                                                           }, useManyToOne: false);
            //cfg.AddFilterDefinition(filterDef);



            cfg.Cache(x =>
            {
                // SysCache is not stable on unit testing
                if (!useUnitTest)
                {
                    x.Provider<NHibernate.Caches.SysCache.SysCacheProvider>();

                    // I don't know why SysCacheProvider is not stable on simultaneous unit testing, 
                    // might be SysCacheProvider is just giving one session factory, so simultaneous test see each other caches
                    // This solution doesn't work: http://stackoverflow.com/questions/700043/mstest-executing-all-my-tests-simultaneously-breaks-tests-what-to-do                    
                }
                else
                {
                    // This is more stable in unit testing
                    x.Provider<NHibernate.Cache.HashtableCacheProvider>();
                }





                // http://stackoverflow.com/questions/2365234/how-does-query-caching-improves-performance-in-nhibernate

                // Need to be explicitly turned on so the .Cacheable directive on Linq will work:                    
                x.UseQueryCache = true;
            });


            if (useUnitTest)
                cfg.SetInterceptor(new NHSqlInterceptor());

            var sf = cfg.BuildSessionFactory();




            //using (var file = new System.IO.FileStream(@"c:\x\ddl.txt",
            //       System.IO.FileMode.Create,
            //       System.IO.FileAccess.ReadWrite))
            //using (var sw = new System.IO.StreamWriter(file))
            //{
            //    new NHibernate.Tool.hbm2ddl.SchemaUpdate(cfg)
            //        .Execute(scriptAction: sw.Write, doUpdate: false);
            //}

            return sf;
        }

        class NHSqlInterceptor : NHibernate.EmptyInterceptor
        {
            // http://stackoverflow.com/questions/2134565/how-to-configure-fluent-nhibernate-to-output-queries-to-trace-or-debug-instead-o
            public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
            {

                Mapper.NHibernateSQL = sql.ToString();
                return sql;
            }

        }

        public static string NHibernateSQL { get; set; }


    } // Mapper



}



Happy Coding!

Friday, November 22, 2013

Add a condition on joined entities in NHibernate

This query is working, but the cross-cutting concern, e.g., localization, is cluttering the main Where clause

var x = 
    (from q in 
        from ps in session.Query<GetProductSold>()
        join pl in session.Query<ProductLanguage>() on ps.ProductId equals pl.ProductLanguageCompositeKey.ProductId
        select new { ps, pl }
    where q.pl.ProductLanguageCompositeKey.LanguageCode == languageCode
    select q).Cacheable();


We can convert that to following, however NHibernate doesn't support this yet:
var x = 
    (from q in 
         from ps in session.Query<GetProductSold>()
         join pl in session.Query<ProductLanguage>().Where(plc => plc.ProductLanguageCompositeKey.LanguageCode == languageCode) on ps.ProductId equals pl.ProductLanguageCompositeKey.ProductId
         select new { ps, pl }                         
     select q).Cacheable();


We have to do it this way:
var x =
    (from q in
         from ps in session.Query<GetProductSold>()
         join pl in session.Query<ProductLanguage>() on new { ps.ProductId, LanguageCode = languageCode } equals new { pl.ProductLanguageCompositeKey.ProductId, pl.ProductLanguageCompositeKey.LanguageCode }
         select new { ps, pl }                         
     select q).Cacheable();

Saturday, November 2, 2013

Managed code success stories

If you think managed code is slow, think twice

C++ :
million lines of code
40 developers
15 years
output: 10k trades per second

Managed code :
6k lines of code*
1 developer
3 months
output: 200k trades per second

http://stackoverflow.com/questions/4257659/c-sharp-versus-c-performance/19505716#19505716

* Wow! 6k lines of F# code (nope, I'm not cursing lol) only, my thesis is just almost that size. The realization that a 6k lines of code could deliver world-class solution to a world-class problem given using the right tool, just humbled the C++ programmer in me; likewise to my C# skills too


Thoughts on managed language vs C++ on the above success of managed code against C++ :

https://twitter.com/jonharrop/status/392415184045633536


Of course there are more success stories on C++ too :

http://www.computerworlduk.com/news/open-source/3260727/london-stock-exchange-in-historic-linux-go-live/

http://www.computerworlduk.com/in-depth/open-source/3246835/london-stock-exchange-linux-record-breaking-system-faces-new-challengers/


Happy Computing! ツ

Monday, October 7, 2013

Thanks SQL Server! My Bonus is Now Higher* than My Salary

Given these two functions:

create function EmployeeMoolahs()
returns table
as
return
    select
        e.EmployeePersonID, e.Bonus, e.Salary
    from Employee e
go
 
create function EmployeeSummary()
returns table
as
return
    select p.FirstName, p.LastName, em.*
    from Person p
    join EmployeeMoolahs() em on p.PersonID = em.EmployeePersonId
go

The output of this query:

select top 5 * from EmployeeSummary() es order by es.EmployeePersonId




Seeing that it's best to present the salary information first before the bonus, we move the Salary information right after the EmployeePersonID.

alter function EmployeeMoolahs()
returns table
as
return
    select
        e.EmployeePersonID, e.Salary, e.Bonus
    from Employee e
go

Then you go back home, didn't even bother to check the output since it's a trivial fix anyway. On the 15th of the month, you receive your salary and you check your pay slip. You are so delighted that they make your bonus equal to your salary, then your jaw dropped, you don't receive any salary. Upon investigating the cause of that anomaly, you found out the erring query:

select top 5 * from EmployeeSummary() es order by es.EmployeePersonId




It yields a wrong output!


Whoa, holy guacamole! Why I don't have any salary? As if, the Bonus and Salary swapped contents. Yeah right, they really are. The presentation concern you tried to fix, by placing Salary right after EmployeePersonId, has an error. The salary is now slotted to bonus field, and the bonus is now slotted salary field.


Checking if your eyes is fooling you, you tried to explicitly select all the columns:

select top 5 es.FirstName, es.LastName, es.EmployeePersonId, es.Salary, es.Bonus from EmployeeSummary() es order by es.EmployeePersonId

But still, you still don't have any salary.




You also check the function you modified if it returns correct information:

select top 5 * from EmployeeMoolahs() em order by em.EmployeePersonId

However, it has correct output:




Then you are thinking what causes the error on EmployeeSummary function? What makes the query wild? Why it's not returning proper information? Hmm.. wild? Maybe it's the wild…card, there's something wild on the wildcard! Got to remove the wildcard on EmployeeSummary()

create function EmployeeSummary()
returns table
as
return
    select p.FirstName, p.LastName, em.*
    from Person p
    join EmployeeMoolahs() em on p.PersonID = em.EmployeePersonId
go

Change that to explicit form:

alter function EmployeeSummary()
returns table
as
return
    select p.FirstName, p.LastName, em.EmployeePersonId, em.Salary, em.Bonus
    from Person p
    join EmployeeMoolahs() em on p.PersonID = em.EmployeePersonId
go

Then you check the result of this query:

select top 5 * from EmployeeSummary() es order by es.EmployeePersonId

It's correct now!



Trying to check the fix if it is just a fluke, we swap again the order of Bonus and Salary of the source function to their original order:

alter function EmployeeMoolahs()
returns table
as
return
    select
        e.EmployeePersonID, e.Bonus, e.Salary
    from Employee e
go

Re-check again:

select top 5 * from EmployeeSummary() es order by es.EmployeePersonId

Below is the result, the columns' information are still correct! The order of fields won't change as we are selecting from EmployeeSummary which has explicitly selected columns. What is important, the information is always correct, the ordering of columns should be done on EmployeeSummary, not on EmployeeMoolahs.





Let's try to investigate the root cause of the error. If you got wildcard in the query of your function, SQL Server is trying to build a metadata for returned fields of that function, as if SQL Server burns a metadata for the returned fields of the query of your function, albeit invisibly.

create function EmployeeMoolahs()
returns table
-- burn these metadata...
(
    Bonus money,
    Salary money
)
-- ...burn
as
return
    select
        e.EmployeePersonID, e.Bonus, e.Salary
    from Employee e
go
 
create function EmployeeSummary()
returns table
-- burn metadata
(
    FirstName nvarchar(max),
    LastName nvarchar(max),
          -- burn these metadata based on the metadata obtained from EmployeeMoolah's wildcard
    EmployeePersonId int,   -- 1
    Bonus money,            -- 2
    Salary money            -- 3
)
-- ...burn
as
return
    select p.FirstName, p.LastName, em.*
     
          -- At the time this EmployeeSummary function is created, this is the order of fields on
         -- EmployeeMoolah()'s wildcard:
     
          -- 1. EmployeePersonId
          -- 2. Bonus
          -- 3. Salary
     
     
    from Person p
    join EmployeeMoolahs() em on p.PersonID = em.EmployeePersonId
go

In fact that is how other RDBMS does it, i.e. you need to explicitly return the metadata of your returned fields. Wildcard problem can be avoided by application developers using other RDBMS as they can see the wildcard could one day not be slotted properly to the correct column metadata, so they tend to be explicit on selecting each columns. They tend to avoid wildcards on functions. This kind of problem is virtually non-existent to them.


So in SQL Server, if we change the order of the fields on the source function (EmployeeMoolahs) of the dependent function(EmployeeSummary), e.g. swap the position of Bonus and Salary, Salary comes before Bonus on EmployeeMoolahs, the EmployeeSummary's metadata will not be updated accordingly, it will not be automatically updated. That is, EmployeeSummary metadata's position will still be the same, i.e. Bonus still comes before Salary. Hence when we issue a query on wildcard-bearing EmployeeSummary function, the wildcard will fall to the wrong slots.


EmployeeSummary() 's Metadata is not automatically updated     EmployeePersonId    Bonus    Salary
EmployeeMoolah()'s expanded wildcard from its swapped columns  EmployeePersonId    Salary   Bonus

So that's how the wildcard can cause problems on SQL Server. Trying to imagine what fields are good to slot on the salary :P


Think of the damages that can be caused by wrong information slotted by the wildcard. IsHired field goes to IsAdmin, OriginalPrice goes to CurrentPrice, PassingScore goes to Score, etc.


* It can happen, try to copy the problem above :P


Sample data:
create table Person
(
 PersonId int identity(1,1) primary key,
 FirstName nvarchar(max) not null,
 LastName nvarchar(max) not null,
);

create table Employee
(
 EmployeePersonId int not null primary key references Person(PersonId),
 Salary money not null,
 Bonus money not null default(0)
);

insert into Person(FirstName, LastName) values
('John', 'Lennon'),
('Paul', 'McCartney'),
('George', 'Harrison'),
('Ringo', 'Starr');


insert into Employee(EmployeePersonId, Salary, Bonus) values
(1, 60000, 20000),
(2, 60000, 0),
(3, 40000, 0),
(4, 40000, 0);




Happy Computing! ツ