Tuesday, 10 February 2015

Automatic mapping for deep object graphs in Entity Framework



Using the great code sample from DevTrends article "Stop using AutoMapper in your source code", I have adjusted the code a bit to support not only up to two layers deep object graphs, but arbitrarily deepth. The limit is a design choice which can be easily increased into deeper levels. The code will automatic generate the mapping code using Expression Trees. Example:


.New DevTrends.QueryableExtensionsExample.StudentSummary(){
    FirstName = $src.FirstName,
    LastName = $src.LastName,
    TutorName = ($src.Tutor).Name,
    TutorAddressStreet = (($src.Tutor).Address).Street,
    TutorAddressMailboxNumber = ((($src.Tutor).Address).Mailbox).Number,
    CoursesCount = ($src.Courses).Count
}


Consider the automatic mapping of an entity in Entity Framework to a flattened model, where the entity is a relatively deep object graph. The tedious mapping code will be boring to write when the number properties grow. If we stick to a convention where we use CamelCasing to denote levels in the object graph, we can address the subproperties arbitrarily deep with this code.

   public class StudentSummary
    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string NotADatabaseColumn { get; set; }

        public string TutorName { get; set; }

        public string TutorAddressStreet { get; set;  }

        public string TutorAddressMailboxNumber { get; set; }

        public int CoursesCount { get; set; }

        public string FullName
        {
            get { return string.Format("{0} {1}", FirstName, LastName); }
        }

    }

Note here that for a given property such as TutorAddressMailboxNumber, the code will look for the property Tutor.Address.Mailbox.Number since the CamelCasing convention used here will check this. The implementation of the Project() method and the To() method on IQueryable<T> is shown next. The code uses Expression Trees to generate the required automatic mapping code. Note that the method Buildbinding method will run when the Expression.Lambda statement is executed, when debugging.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;

namespace DevTrends.QueryableExtensionsExample
{

    public static class QueryableExtensions
    {
        public static ProjectionExpression<TSource> Project<TSource>(this IQueryable<TSource> source)
        {
            return new ProjectionExpression<TSource>(source);
        }
    }

    public class ProjectionExpression<TSource>
    {
        private static readonly Dictionary<string, Expression> _expressionCache = new Dictionary<string, Expression>();

        private readonly IQueryable<TSource> _source;

        public ProjectionExpression(IQueryable<TSource> source)
        {
            _source = source;
        }

        public IQueryable<TDest> To<TDest>()
        {
          var queryExpression = GetCachedExpression<TDest>() ?? BuildExpression<TDest>();

            return _source.Select(queryExpression);
        }        

        private static Expression<Func<TSource, TDest>> GetCachedExpression<TDest>()
        {
            var key = GetCacheKey<TDest>();

            return _expressionCache.ContainsKey(key) ? _expressionCache[key] as Expression<Func<TSource, TDest>> : null;
        }

        private static Expression<Func<TSource, TDest>> BuildExpression<TDest>()
        {
            var sourceProperties = typeof(TSource).GetProperties();
            var destinationProperties = typeof(TDest).GetProperties().Where(dest => dest.CanWrite);
            var parameterExpression = Expression.Parameter(typeof(TSource), "src");
            
            var bindings = destinationProperties
                                .Select(destinationProperty => BuildBinding(parameterExpression, destinationProperty, sourceProperties))
                                .Where(binding => binding != null);

            var expression = Expression.Lambda<Func<TSource, TDest>>(Expression.MemberInit(Expression.New(typeof(TDest)), bindings), parameterExpression);

            var key = GetCacheKey<TDest>();

            _expressionCache.Add(key, expression);

            return expression;
        }        

        private static MemberAssignment BuildBinding(Expression parameterExpression, MemberInfo destinationProperty, IEnumerable<PropertyInfo> sourceProperties)
        {
            var sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == destinationProperty.Name);

            if (sourceProperty != null)
            {
                return Expression.Bind(destinationProperty, Expression.Property(parameterExpression, sourceProperty));
            }

            var propertyNameComponents = SplitCamelCase(destinationProperty.Name);

            if (propertyNameComponents.Length >= 2)
            {
                sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == propertyNameComponents[0]);
                if (sourceProperty == null)
                    return null; 

                var propertyPath = new List<PropertyInfo> { sourceProperty };
                TraversePropertyPath(propertyPath, propertyNameComponents, sourceProperty);

                if (propertyPath.Count != propertyNameComponents.Length)
                    return null; //must be able to identify the path 

                 MemberExpression compoundExpression = null;
                
                for (int i = 0; i < propertyPath.Count; i++)
                {
                    compoundExpression = i == 0 ? Expression.Property(parameterExpression, propertyPath[0]) : 
                        Expression.Property(compoundExpression, propertyPath[i]); 
                }

                return compoundExpression != null ? Expression.Bind(destinationProperty, compoundExpression) : null; 
            }

            return null;
        }

        private static List<PropertyInfo> TraversePropertyPath(List<PropertyInfo> propertyPath, string[] propertyNames, 
            PropertyInfo currentPropertyInfo, int currentDepth = 1)
        {
            if (currentDepth >= propertyNames.Count() || currentPropertyInfo == null)
                return propertyPath; //do not go deeper into the object graph

            PropertyInfo subPropertyInfo = currentPropertyInfo.PropertyType.GetProperties().FirstOrDefault(src => src.Name == propertyNames[currentDepth]); 
            if (subPropertyInfo == null)
                return null; //The property to look for was not found at a given depth 

            propertyPath.Add(subPropertyInfo);

            return TraversePropertyPath(propertyPath, propertyNames, subPropertyInfo, ++currentDepth);
        }

        private static string GetCacheKey<TDest>()
        {
            return string.Concat(typeof(TSource).FullName, typeof(TDest).FullName);
        }

        private static string[] SplitCamelCase(string input)
        {
            return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim().Split(' ');
        }

    }    
}

The database is initialized with some sample data, the sample uses an Entity Framework CodeFirst database.

using System;
using System.Data.Entity;
using System.Linq;

namespace DevTrends.QueryableExtensionsExample
{
    class Program
    {
        static void Main()
        {
            Database.SetInitializer(new TestDataContextInitializer());

            using (var context = new StudentContext())
            {
                ExampleOne(context);

                ExampleTwo(context);                
            }

            Console.Write("\nPress a key to exit: ");
            Console.Read();
        }

        private static void ExampleOne(StudentContext context)
        {
            // This is the kind of projection code that we are replacing

            //var students = from s in context.Students
            //               select new StudentSummary
            //                          {
            //                              FirstName = s.FirstName,
            //                              LastName = s.LastName,
            //                              TutorName = s.Tutor.Name,
            //                              CoursesCount = s.Courses.Count
            //                          };


            // The line below performs exactly the same query as the code above.

            var students = context.Students.Project().To();

            Console.Write("Example One\n\n");

            // Uncomment this to see the generated SQL. The extension method produces the same SQL as the projection.
            //Console.WriteLine("SQL: \n\n{0}\n\n", students);

            foreach (var student in students)
            {
                Console.WriteLine("{0} is tutored by {1} in {2} subject(s).", student.FullName, student.TutorName, student.CoursesCount);
            }
        }

        private static void ExampleTwo(StudentContext context)
        {
            //var students = from s in context.Students
            //               select new AnotherStudentSummary()
            //               {
            //                   FirstName = s.FirstName,
            //                   LastName = s.LastName,
            //                   Courses = s.Courses
            //               };

            var students = context.Students.Project().To();

            Console.WriteLine("\nExample Two\n");
            
            //Console.WriteLine("SQL: \n\n{0}\n\n", students);

            foreach (var student in students)
            {
                var coursesString = string.Join(", ", student.Courses.Select(s => s.Description).ToArray());
                Console.WriteLine("{0} {1} takes the following courses: {2}", student.FirstName, student.LastName, coursesString);
            }
        }
    }        
}



using System;
using System.Data.Entity;
using System.Linq;

namespace DevTrends.QueryableExtensionsExample
{
    class Program
    {
        static void Main()
        {
            Database.SetInitializer(new TestDataContextInitializer());

            using (var context = new StudentContext())
            {
                ExampleOne(context);

                ExampleTwo(context);                
            }

            Console.Write("\nPress a key to exit: ");
            Console.Read();
        }

        private static void ExampleOne(StudentContext context)
        {
            // This is the kind of projection code that we are replacing

            //var students = from s in context.Students
            //               select new StudentSummary
            //                          {
            //                              FirstName = s.FirstName,
            //                              LastName = s.LastName,
            //                              TutorName = s.Tutor.Name,
            //                              CoursesCount = s.Courses.Count
            //                          };


            // The line below performs exactly the same query as the code above.

            var students = context.Students.Project().To<StudentSummary>();

            Console.Write("Example One\n\n");

            // Uncomment this to see the generated SQL. The extension method produces the same SQL as the projection.
            //Console.WriteLine("SQL: \n\n{0}\n\n", students);

            foreach (var student in students)
            {
                Console.WriteLine("{0} is tutored by {1} in {2} subject(s).", student.FullName, student.TutorName, student.CoursesCount);
            }
        }

        private static void ExampleTwo(StudentContext context)
        {
            //var students = from s in context.Students
            //               select new AnotherStudentSummary()
            //               {
            //                   FirstName = s.FirstName,
            //                   LastName = s.LastName,
            //                   Courses = s.Courses
            //               };

            var students = context.Students.Project().To<AnotherStudentSummary>();

            Console.WriteLine("\nExample Two\n");
            
            //Console.WriteLine("SQL: \n\n{0}\n\n", students);

            foreach (var student in students)
            {
                var coursesString = string.Join(", ", student.Courses.Select(s => s.Description).ToArray());
                Console.WriteLine("{0} {1} takes the following courses: {2}", student.FirstName, student.LastName, coursesString);
            }
        }
    }        
}


I have tested the code with a depth of four for the object graph, which will be sufficient in most cases. The maximum level is 10 just to prevent the recursion for giving a possible stack overflow, but the limit can be easily adjusted here. Using the code provided here, it will be easier to automatic map entity framework code into models. The code provided here should be used for retrieving data, such as selects. Note that we are short-circuiting the change tracking of Entity Framework here and must stick to a CamelCasing convention to get the automatic mapping from Entity to the flattened model, that must keep to the CamelCasing convention. .

Download the sample code here

Source code in Visual Studio 2012 solution

Example integration test (The ObjectContextManager.ScopedOpPlanDataContext object here is an example ObjectContext)


        [Test]
        [Category(TestCategories.IntegrationTest)]
        public void ProjectToPerformsExpected()
        {
            using (var context = ObjectContextManager.ScopedOpPlanDataContext)
            {
                var globalParameters = context.GlobalParameters.Project().To<GlobalParametersDataContract>().ToList();
                Assert.IsNotNull(globalParameters);
                CollectionAssert.IsNotEmpty(globalParameters);
            }
        }


Monday, 24 November 2014

Reducing complexity in Entity Framework queries with IQuyerable and Linqkit

I have added a presentation written in Norwegian that explains how one can reduce complexity in Entity Framework, using IQuyerable decomposition and Linqkit functionality. Reduce Entity Framework

Tuesday, 14 October 2014

Asynchronous Entity Framework operations

Sample code:

using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;

namespace TestAsyncEntityFramework6
{
   
    public class CustomHelper
    {

        public static List<Customer> SelectAll()
        {
            using (var context = new NorthWindEntities())
            {
                var query = from c in context.Customers
                    orderby c.CustomerID ascending
                    select c;
                return query.ToList(); 
            }
        }

        public static async Task<List<Customer>> SelectAllAsync()
        {
            using (var context = new NorthWindEntities())
            {
                var query = from c in context.Customers
                    orderby c.CustomerID ascending
                    select c;
                return await query.ToListAsync();
            }
        }

        public static async Task<Customer> SelectByIdAsync(string id)
        {
            using (var context = new NorthWindEntities())
            {
                var query = from c in context.Customers
                    where c.CustomerID == id
                    select c;
                Customer obj = await query.SingleOrDefaultAsync();
                return obj;
            }
        }

        public static async Task<string> InsertAsync(Customer obj)
        {
            using (var context = new NorthWindEntities())
            {
                //try
                //{
                    context.Customers.Add(obj);
                    await context.SaveChangesAsync();
                    return "Customer added successfully!";
                //}
                //catch (Exception err)
                //{
                //    return "Crash"; 
                //}
            }
        }

        public static async Task<string> UpdateAsync(Customer obj)
        {
            using (var context = new NorthWindEntities())
            {
                Customer existing = await context.Customers.FindAsync(obj.CustomerID);
                existing.CompanyName = obj.CompanyName;
                existing.Country = obj.Country;
                await context.SaveChangesAsync();
                return "Customer updated successfully"; 
            }
        }

        public static async Task<string> DeleteAsync(string id)
        {
            using (var context = new NorthWindEntities())
            {
                Customer existing = await context.Customers.FindAsync(id);
                context.Customers.Remove(existing);
                await context.SaveChangesAsync();
                return "Customer deleted successfully!"; 
            }
        }

        public static async Task<Customer> GetCustomerByIdAsync(string id)
        {
            using (var context = new NorthWindEntities())
            {
                Customer existing = await context.Customers.FindAsync(id);
                return existing; 
            }
        }

    }
}


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestAsyncEntityFramework6
{
    class Program
    {

        static void Main(string[] args)
        {
            Stopwatch sw = Stopwatch.StartNew();
            var task = CustomHelper.SelectAllAsync();
            task.Wait();
            Console.WriteLine("Got data!");
            List<Customer> data = task.Result;
            Console.WriteLine(data.Count);
            Console.WriteLine("Async op took: " + sw.ElapsedMilliseconds);
            sw.Stop();
            sw.Start();
            //data = 
            //var data =  CustomHelper.SelectAll();
            //Console.WriteLine("Got data!");
            //Console.WriteLine(data.Count);
            //Console.WriteLine("Sync operation took: " + sw.ElapsedMilliseconds);

            var c = new Customer {CustomerID = "TEIT", Country = "Burkina Faso", CompanyName = "Tore Aurstad IT"};
            try
            {
                var task2 = CustomHelper.InsertAsync(c);
                task2.Wait();
                Console.WriteLine(task2.Result);
            }
            catch (AggregateException ae)
            {
                Console.WriteLine(ae.Message);
            }

            var c3 = CustomHelper.GetCustomerByIdAsync("TEIT");
            c3.Wait();
            Console.WriteLine(c3.Result.Country);

            c3.Result.Country = "Norway"; 

            var c4 = CustomHelper.UpdateAsync(c3.Result);
            c4.Wait();
            Console.WriteLine(c4.Result);

            var c5 = CustomHelper.DeleteAsync(c3.Result.CustomerID);
            c5.Wait();
            Console.WriteLine(c5.Result); 

            Console.WriteLine("Press any key to continue ...");
            Console.ReadKey(); 
        }

    }
}


Monday, 13 October 2014

EntityFramework.Extended library performance capabilities

EntityFramework.Extended is an additional library that can be installed with Entity Framework 6 and give additional performance capabilities with Entity Framework, such as: - Batch updates - Batch deletes - Batch queries - Query caching The following demo code shows some simple usage of EntityFramework.Extended library to use these new capabilities which the "Core" Entity Framework lacks good support for:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using EntityFramework.Caching;
using EntityFramework.Extensions;

namespace TestEntityFrameworkExtended
{

    /// <summary>
    /// Samples based on the EntityFramework.Extended Github site here: https://github.com/loresoft/EntityFramework.Extended
    /// </summary>
    /// <remarks>Nuget package page here: https://www.nuget.org/packages/EntityFramework.Extended/ Created by LoreSoft, open source
    /// Entity Framework 6 assumed to be a requirement here (TODO: Inspect, don't expect)</remarks>
    class Program
    {

        static void Main()
        {
            DemoBatchUpdates();
            DemoBatchQueries();
            DemoQueryResultCache(); 
            DemoBatchDeletion();
        }

        private static void DemoQueryResultCache()
        {
            using (var ctx = new SampleDbContext())
            {
                var vipUsers =
                    ctx.Users.Where(u => u.MiddleName == "VIPUser").FromCache(
                        CachePolicy.WithDurationExpiration(TimeSpan.FromSeconds(60)), new[]{ "Viktigperer" });
                //PrintUsers("Query cache resultat:", vipUsers);
                foreach (var i in Enumerable.Range(1, 20))
                {
                    if (i == 10)
                        CacheManager.Current.Expire("Viktigperer");
                    vipUsers = ctx.Users.Where(u => u.MiddleName == "VIPUser").FromCache();
                    Console.WriteLine("# of Vip Users: " + vipUsers.Count());
                    Thread.Sleep(1000);
                }
            }

            //Note that the FromCache call now caches the vip users indefinately (no expiration), until one explicitly expires the 
            //tagged cache (or avoid using the FromCache extension). Therefore, tagging cached results are good practice such that the
            //cache can be evicted by using CacheManager.Current.Expire. The cache tags can be set to const strings for example
            //The FromCache makes it easy to cache particular result sets that are known to be accessed by many users and therefore
            //can be cached. For example type registers in OpPlan or Theaters. The database round trip will then be avoided and the 
            //result is cached in the w3wp process server side. Reducing traffic and returning cached results will scale better, but 
            //the cache will also use more memory. Be critical to what to cache (OpPlan 4 Theaters for example can be cached and then
            //expired on demand using CacheManager.Current.Expire, reducing overall traffic between OpPlanWAS and database and also
            //pressure on the CPU) 
            using (var ctx = new SampleDbContext())
            {
                var vipUsers =
                    ctx.Users.Where(u => u.MiddleName == "VIPUser").FromCache();
            }
        }

        private static void DemoBatchQueries()
        {
            using (var ctx = new SampleDbContext())
            {
                //Building up a batch query using the future extension methods. The first call using .Value or .ToList
                //will then start all batched queries, avoiding round trips to database 
                var aQuery = ctx.Users.Where(u => u.FirstName == "Pedro" && u.MiddleName == "VIPUser").FutureFirstOrDefault();
                var bQuery =
                    ctx.Users.Where(u => u.FirstName == "Olivini").FutureFirstOrDefault();
                User a = aQuery.Value;
                User b = bQuery.Value; 
                PrintUsers("Batch query results: ", new[]{ a, b});
            }
        }

        private static void DemoBatchUpdates()
        {
            using (var ctx = new SampleDbContext())
            {
                ctx.Users.Delete(); //clear 

                ctx.Users.Add(new User
                {
                    FirstName = "Pedro",
                    LastName = "Hauginho"
                });
                ctx.Users.Add(new User
                {
                    FirstName = "Julio",
                    LastName = "Cannevaro"
                });
                ctx.Users.Add(new User
                {
                    FirstName = "Jono",
                    MiddleName = "Pedro",
                    LastName = "Salvatini"
                });
                ctx.Users.Add(new User
                {
                    FirstName = "Olivini",
                    LastName = "Hepsado"
                });
                ctx.SaveChanges();

                ctx.Users.Where(u => u.FirstName.Length <= 4).Update(u => new User {MiddleName = "VIPUser"});  //Batch update
            }

            //Important! Reading the database AFTER a batch update should create a NEW db context instance to get refreshed data!
            using (var ctx = new SampleDbContext())
            {
                PrintUsers("Batch update result: ", ctx.Users);
            }
        }

        private static void PrintUsers(string header, IEnumerable<User> users)
        {
            Console.WriteLine(Environment.NewLine + header);
            foreach (var user in users)
            {
                Console.WriteLine("{0} {1} {2}", user.FirstName, user.MiddleName, user.LastName);
            }

            Console.WriteLine("Press the Any Key to Continue ... Now where's that Any Key?");
            Console.ReadKey();
        }

        private static void DemoBatchDeletion()
        {
            using (var ctx = new SampleDbContext())
            {

                ctx.Users.Delete(); //clear (first demo of batch delete)

                ctx.Users.Add(new User
                {
                    FirstName = "Rudolf",
                    LastName = "Holgedra"
                });
                ctx.Users.Add(new User
                {
                    FirstName = "Mario",
                    LastName = "Madraminho"
                });
                ctx.Users.Add(new User
                {
                    FirstName = "Vittorio",
                    MiddleName = "Pedro",
                    LastName = "Salinas"
                });
                ctx.Users.Add(new User
                {
                    FirstName = "Fernando",
                    LastName = "Torres"
                });
                ctx.SaveChanges();

                ctx.Users.Where(u => u.FirstName == "Mario" || u.MiddleName == "Pedro").Delete(); //second batch delete 

                PrintUsers("Batch delete users result: ", ctx.Users.ToList());

            }

        }

    }

}




using System.Data.Entity;

namespace TestEntityFrameworkExtended
{
    
    public class SampleDbContext : DbContext
    {

        public DbSet<User> Users { get; set; }

    }

}


namespace TestEntityFrameworkExtended
{
    
    public class User
    {

        public int UserId { get; set; }

        public string LastName { get; set; }

        public string FirstName { get; set; }

        public string MiddleName { get; set; }

    }

}


After running the code above, one experience from the batch updates was that one should create a new db context / object context such that the data is refreshed after the batch update is performed. The batch queries will send multiple queries towards the database simultanously. Note that after a batch delete, also a new db context / object context should be instantiated. The query caching mechanism will indefinately cache all queries with the same query content with the default FromCache() overload. To expire a query cache, use CacheManager.Current.Expire("MyCacheKey"). It is a good practice to tag the cache and also set a timeout expiration, which can be set either absolute or sliding. Caching queries will mean that your serverside will spend more memory, such as the w3wp worker process running WCF services. Do not cache large result sets and also be aware when the cache should be evicted / expired such that correct, fresh data can be reloaded. Using query caching, batch updates, batch deletes and batch queries, EntityFramework.Extended gives EF a good performance boost. Entity Framework should consider incorporating these features into EF, as they are general performance enhancements. Extra note - It is not necessary to create a new DbContext if you explicitly refresh the context like for example the following sample code:

    //Alternatively force update after batch update (same for batch delete)
    ObjectContext octx = ((IObjectContextAdapter) ctx).ObjectContext; 
    octx.Refresh(RefreshMode.StoreWins, ctx.Users);

Monday, 29 September 2014

Java 8 - using collect in the Stream API

The following code povides some examples how to do collect() in the Java Stream API.



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.cert.PKIXRevocationChecker.Option;
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 *
 * @author José
 */
public class DataAndTime {

    public static void main(String[] args) {

        List<Person> persons = new ArrayList<>();
        
        try (
            BufferedReader reader = 
                new BufferedReader(
                    new InputStreamReader(
                        DataAndTime.class.getResourceAsStream("people.txt")));
            Stream<String> stream = reader.lines();
        ) {
            
            stream.map(
               line -> {
                   String[] s = line.split(" ");
                   String name = s[0].trim();
                   int year = Integer.parseInt(s[1]);
                   Month month = Month.of(Integer.parseInt(s[2]));
                   int day = Integer.parseInt(s[3]);
                   Person p = new Person(name, LocalDate.of(year, month, day));
                   persons.add(p);
                   return p;
               })
               .forEach(System.out::println);
            
            Optional<Person>opt = persons.stream().filter(p -> p.getAge() >= 20)
            .min(Comparator.comparing(Person::getAge));
            
            System.out.println("Youngest person among all persons: " + opt); 
            
            Optional<Person>optSecond = persons
              .stream()
              .max(Comparator.comparing(Person::getAge)); 
            
            System.out.println("Oldest person among all persons: " + optSecond);    
            
            Map<Integer, Long> map = 
              persons.stream()
              .collect(Collectors.groupingBy(
                Person::getAge, Collectors.counting())); 
            
            System.out.println(map);
            
            Map<Integer, List<String>> mapSecond = 
              persons.stream()
              .collect(Collectors.groupingBy(
                Person::getAge, 
                Collectors.mapping(
                  Person::getName,
                  Collectors.toList()
                  )
                )); 
            
            System.out.println(mapSecond);
            
            Map<Integer, Set<String>> mapThird = 
              persons.stream()
              .collect(Collectors.groupingBy(
                Person::getAge, 
                Collectors.mapping(
                  Person::getName,
                  Collectors.toCollection(TreeSet::new)
                  )
                )); 
            
            System.out.println(mapThird);
            
            Map<Integer, String> mapFourth = 
              persons.stream()
              .collect(Collectors.groupingBy(
                Person::getAge, 
                Collectors.mapping(
                  Person::getName,
                  Collectors.joining(", ")
                  )
                )); 
            
            System.out.println(mapFourth);
            
            
            
        } catch (IOException ioe) {
            System.out.println(ioe);
        }

        LocalDate now = LocalDate.of(2014, Month.MARCH, 12);
        
        persons.stream().forEach(
                p -> {
                    Period period = Period.between(p.getDateOfBirth(), now);
                    System.out.println(p.getName() + " was born " +
                            period.get(ChronoUnit.YEARS) + " years and " + 
                            period.get(ChronoUnit.MONTHS) + " months " + 
                            "[" + p.getDateOfBirth().until(now, ChronoUnit.MONTHS) 
                            + " months]"
                            );
                    
                });
    }
}

The definition of Person class:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */


import java.time.LocalDate;
import java.time.chrono.IsoChronology;

/**
 *
 * @author José
 */
public class Person {
    
    private String name;
    private LocalDate dateOfBirth;
    
    public Person(){}
    
    public Person(String name, LocalDate dateOfBirth) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
    }

    public String getName() {
        return name;
    }

    public LocalDate getDateOfBirth() {
        return dateOfBirth;
    }
    

 public int getAge(){
  return dateOfBirth.until(IsoChronology.INSTANCE.dateNow()).getYears();  
 }

    @Override
    public String toString() {
        return "Person{" + "name=" + name + ", dateOfBirth=" + dateOfBirth + '}';
    }
}


The sample persons are collected from this text file:

Sarah 1999 12 15
Philip 1993 8 12
Beth 1991 6 5
Simon 1990 3 23
Nina 1991 7 12
Allan 1985 2 14
Leonard 1996 10 27
Barbara 1988 4 19

Here is a sample Stream API query to retrieve a comma separated string of the names of the persons in the list, as an additional example of how to retrieve information from an entire collection without using collect, but with map and reduce:

   Optional personNames = persons.stream().map(p->p.getName()).reduce((p1,p2)-> p1 + "," + p2);
            System.out.println(personNames);       

Sunday, 28 September 2014

Using immutability in Java to support functional programming and avoid side effects

The following article is based on Venkat Subramaniam's article "Programming with immutability in Java" in "The Developer" magazine No. 3 2013. The code presented below shows how Java 8 supports immutability with different techniques. Consider the following Java 8 code sample:

import java.util.*;
import java.util.stream.*;

public class ImmutableTest1 {

 public static void printTotal(Stream<Integer> values){
  long start = System.nanoTime(); 
  final int total = 
   values
   .filter(value->value>3)
   .filter(value->isEven(value))
   .mapToInt(value->value*2)
   .sum(); 

   long end = System.nanoTime(); 
   System.out.printf("Total: %d Time: %.3f seconds\n", total, (end - start)/1.0e9); 

 }

 public static boolean isEven(int number){
  try {
   Thread.sleep(100); 
  }
  catch (Exception ex){   
   System.out.println(ex.getMessage());
  }
  return number % 2 == 0; 
 }

 public static void main(String[] args){
  List<Integer>values = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
  printTotal(values.stream());
  printTotal(values.parallelStream());
  
  for (int element : values) {
   Runnable runnable = () -> System.out.println(element);
   runnable.run();
  }
  
  List<String> namesList = Arrays.asList("Jake", "Raju", "Kim", "Kara", "Paul", "Brad", "Mike"); 
  System.out.println("Found a 3 letter name?: " + 
    namesList.stream().anyMatch(name->name.length() == 3)); 
  
  System.out.println("Found Kim?: " + 
    namesList.stream().anyMatch(name-> name.contains("Kim")));
 }

}

The code above shows how library functions in the Stream API of Java 8 avoids the use of mutable variables by the functional programming code style of chaining. Further, note that the use of a classical for loop prohibits the loop creating new Runnable instances, while the for loop over a collection allows this. The error given for classical for loop is: Local variable i defined in an enclosing scope must be final or effectively final ImmutableTest1.java /ImmutabilityTest1/src line 36 Java Problem given this code:
 for (int i = 0; i < values.size(); i++) {
   Runnable runnable = () -> System.out.println(values.get(i));
   runnable.run();
        }
In addition, recursion is a technique that provides immutability to your code, by avoiding use of mutable control variables, example follows:

import java.util.Scanner;


public class Guesser {
 
 final static int target = (int) (Math.random() * 100); 
 
 public static void play(final int attempts){
  System.out.print("Enter your guess:");
  final int guess = new Scanner(System.in).nextInt();
  if (guess < target)
   System.out.println("Aim higher"); 
  if (guess > target)
   System.out.println("Aim lower");
  
  if (guess == target)
   System.out.printf("You got it in %d attempts\n", attempts);
  else 
   play(attempts+1);
  
 }
 
 public static void main(String[] args){
  System.out.println("I've selected a number, can you guess?"); 
  play(1);
 }

}