Felice Pollano Blog

The Official Fatica Labs Blog

About the author

Author Name is someone.
E-mail me Send mail

Recent comments

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008

How To: Bind a Linq to SQL query results to a VirtualMode ListView

The ListView VirtualMode allow very efficient data display of very large resultset. We can mix this ability with Linq Skip(n) and Take(m) to bind a IQueryable<T> to a virtual list view.  To efficently avoid to query the underlaying database, we have to cache in some way the results with some paging strategy.

Below the solution I used:

public class LinqPager<T>

{

Dictionary<int, List<T>> pages = new Dictionary<int,List<T>>();

IQueryable<T> queryable;

int pageSize;public LinqPager(IQueryable<T> queryable,int pageSize)

{

this.pageSize = pageSize;

this.queryable = queryable;

}

public void InvalidatePages()

{

pages.Clear();

}

public T this[int index]

{

get {int page, offset;

page = index / pageSize;

offset = index % pageSize;

if (!pages.ContainsKey(page))

{

pages[page] =
new List<T>(pageSize);int start = pageSize * page;

pages[page].AddRange(queryable.Skip<T>(start).Take<T>(pageSize));

}

return pages[page][offset];

}

}

}

The class above has a constructor taking an IQueryable<T> and a page size. It will query the DB with chunks pageSize long, and provide an indexer to randomly access the result. This accessor can be used to fill the item on the RetrieveVirtualItem event.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Posted by Felice on Friday, May 16, 2008 2:43 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Smart Client Software Factory: CAB Extensions for WPF

 

We can found more information here. 
The new namespace to investigate inside the library is Microsoft.Practices.CompositeUI.WPF .

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Felice on Tuesday, May 13, 2008 2:15 PM
Permalink | Comments (0) | Post RSSRSS comment feed

The future of CAB for rich clients: the MVC libraries scenario

An interesting post here. Unfortunately there aren't a wide scope solution that could target easily both WPF and Winform.

Some ineteresting links about this topic:

Prism : targets WPF. Some features are:

  • "Enabled TFS Integration for releases
  • IMetadataInfo removed from the Prism framework. The application developer is now responsible for providing a model in order for the headers to bind to. We provide guidance on how to accomplish this in the RI though. Reason: flexibility.
  • MultiDispatchCommand renamed to CompositeCommand
  • DelegateCommand is now generic for compile time check of the parameter type (not at the XAML level though)
  • Bootstrapper refactored for unit testing. Added module initialize.
  • Random market feeds updates the UI continuously.
  • Ability to add named views to regions that you can easily retrieve later.
  • Added logger interface and default enterprise libary logger implementation
  • Several more refactorings and consistency fixes (lots!).
  • New UI Composition QuickStart. Demonstrates shell, global, local regions, and views. Note: This Quickstart uses a branch of the Prism framework source. We will be merging into the main."

Caliburn : targets WPF. Some features are:

  • Improved asynchronous programming experience for actions.
  • Asynchronous module loading
  • Eager and lazy loaded modules with lazy loaded commands/presenters.
  • New isolation options for modules: None or Container-based.
  • Scoped UI composition and data storage.
  • Two different mechanisms for event aggregation; take your pick.  Choose a string-based mechanism similar to CAB or a strongly-typed message-based mechanism.
  • Support for triggering actions with routed events.
  • Support for action message forwarding.
  • IActionMessageHandler for advanced action message scenarios.
  • Support for Windsor/StructureMap/Spring.NET and now Unity containers.
  • Various bug fixes, including several that caused (the feeble) VS designer to break 

Smart Client Software Factory – April 2008 : The CAB evolution on Visual Studio 2008.

behind the scenes some projects as Acropolis disappear, and the WPF Composite Client seems not so active even if here we read that something shoul ship at the end of2008.

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,
Posted by Felice on Tuesday, May 13, 2008 11:31 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Customizing private path and config file with CreateApplicationHost

Hosting asp.net pipeline in .NET is easy, just a call to CreateApplicationHost and we can expose some web infrastructure in our application. Unfortunately, the plain call does not satisfy some requirements:

  • We need to have a separate web.config file for the hosted asp application ( why don't use the application config file itself ? )
  • We need to create a bin subfolder containing the assembly the asp subsystem needs to load.

If we need to change this, we need  to change  the AppDomainSetup parameters for the newly created appdomain. There was a smart hack prior to ASP.NET 2.0 by Rick Stral, but does not work anymore, and just cutting some reflector output to emulate what CreateApplicationHost does internally is too complex.

The solution I propose here is Intercept AppDomain creation and setup the right parameters for us. What we need is a custom AppDomainManager, to inject in our application and let it change the domain setup parameters.

 

 

 public class Manager:AppDomainManager
{
public override AppDomain CreateDomain(string friendlyName
, System.Security.Policy.Evidence securityInfo
, AppDomainSetup appDomainInfo)
{
string s = System.Environment.GetEnvironmentVariable("AD_INTERCEPT_CREATION");
if (!string.IsNullOrEmpty(s) && s == "Y")
{
if (appDomainInfo == null)
appDomainInfo = new AppDomainSetup();
appDomainInfo.ApplicationBase = System.Environment.GetEnvironmentVariable("AD_INTERCEPT_APPBASE",EnvironmentVariableTarget.Process);
appDomainInfo.PrivateBinPath = System.Environment.GetEnvironmentVariable("AD_INTERCEPT_PRIVATE_BIN_PATH", EnvironmentVariableTarget.Process);
appDomainInfo.ConfigurationFile = System.Environment.GetEnvironmentVariable("AD_INTERCEPT_CONFIG_FILE", EnvironmentVariableTarget.Process); ;
}
return base.CreateDomain(friendlyName, securityInfo, appDomainInfo);
}

}

  We need to put this class in a separate assembly, and register it in the GAC. Both these steps are mandatory. The interceptor shown before, just check if there is a process environment variable AD_INTERCEPT_CREATION with a "Y" value. If so, it uses the AD_INTERCEPT_APPBASE,AD_INTERCEPT_PRIVATE_BIN_PATH,AD_INTERCEPT_CONFIG_FILE variables to set the app domain parameters.

Next step is making our application aware of our AppDomainManager. This is done by .NET understood environment variable.

APPDOMAIN_MANAGER_ASM = "the full name with version and public key token of the assembly containing our AppDomainManager "

APPDOMAIN_MANAGER_TYPE = "the type name of our AppDomainManager class"  

This is not a soo easy step. We need some strategy to do it automatically, just because if we set these variable after our process is started, they simple does no effect. After some searching I found the solution here. Basically the trick is: start the process, understand if our application manager is the one we want, if not, shell a new process with the environment variables set.

 Lets have an example:

 

 

static void Main()
{
AppDomainManager domainManager = AppDomain.CurrentDomain.DomainManager;
if (domainManager != null && domainManager.GetType() == typeof(MyADManager.Manager))
{
if (Environment.GetEnvironmentVariable("DEBUG_CHILD", EnvironmentVariableTarget.Process) == "Y")
{
Debugger.Break();
}
//.... "real" application code here
}
else
{
ProcessStartInfo psi = new ProcessStartInfo(Assembly.GetExecutingAssembly().Location, Environment.CommandLine);
// setup the AppDomainManager environment variables 
psi.UseShellExecute = false; 
psi.EnvironmentVariables["APPDOMAIN_MANAGER_ASM"] = "assembly containing AppDomainManager";
psi.EnvironmentVariables["APPDOMAIN_MANAGER_TYPE"] = "MyADManager.Manager";
if( Debugger.IsAttached )
psi.EnvironmentVariables["DEBUG_CHILD"] = "Y";
Process process = Process.Start(psi); 
}
}

 Please not thethat we need to check if a debugger is attached, so we can break the new process instance to attach a debugger again.

Now we almost done. Next and last step is creating the Application Host:

 

 FileInfo fileInfo = new
FileInfo(Assembly.GetExecutingAssembly().Location);
System.Environment.SetEnvironmentVariable("AD_INTERCEPT_CREATION", "Y", EnvironmentVariableTarget.Process);
System.Environment.SetEnvironmentVariable("AD_INTERCEPT_APPBASE", fileInfo.DirectoryName, EnvironmentVariableTarget.Process);
System.Environment.SetEnvironmentVariable("AD_INTERCEPT_PRIVATE_BIN_PATH", fileInfo.DirectoryName, EnvironmentVariableTarget.Process);
System.Environment.SetEnvironmentVariable("AD_INTERCEPT_CONFIG_FILE", "myapp.exe.config", EnvironmentVariableTarget.Process);
//set the app base and the config file
_host = (Host)ApplicationHost.CreateApplicationHost(typeof(Host), _virtualPath, _physicalPath);
System.Environment.SetEnvironmentVariable("AD_INTERCEPT_CREATION", "N", EnvironmentVariableTarget.Process);
_host.Configure(this, Port, _virtualPath, _physicalPath, InstallPath);

 In the example above, we created our ASP.NET pipeline sharing the application config file, and probing the application path for assembly loading. So we have nor more needing of an extra web.config file and bin subfolder.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,
Posted by Felice on Friday, November 23, 2007 4:37 PM
Permalink | Comments (1) | Post RSSRSS comment feed

Competing with legacy code

I'm not new at that kind of experience, always in my life I did the challenge of refactoring some old application, and many times I found something frustrating in it. Does not really matter to me, just because I enjoy to do it, but sometimes I really feel pain. Just some point to clarify what generally happen.

  • Even if there is no design in the legacy stuff you are trying to port, you will find smart people telling you the design do exists and is just too huge to be understood by a single person.
  • Functionsare are hidden: I personally looked at code that do the real job in some nested "catch" block.
  • Bug becames rules: as a smart idea you will find some bad design issue to became a rule to follow. Still today I have code that is not able to work with long file names. There is a lot of almost not working code dealing with the 8+3 dos alias to avoid to refactor a "well designed" fixed length binary record.
  • Managers does not known about technical details: I still find some difficult to explain what is the real difference between dealing with an Ms Access database and a server based database engine ( doesn't matter which one ).
  • Developers cost nothing: Every people is able to desing software. I have some code to show of a self describing senior developer who produced an home made OR/M based on a nested switch case ( first level an enum for the query, second level the DB type ) payed the same as me. I know I could be a little too ambicious, but I felt me crying when I look at that kind of code.
  • Success of legacy: In my experience, the project who earn money was almost messy stuff, created by modifying the modifications, without any kind of design, in which a good portion of the function work on a luck based approach.

Currently rated 3.0 by 1 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by felice on Wednesday, November 14, 2007 11:28 PM
Permalink | Comments (1) | Post RSSRSS comment feed

Ayende è sul mercato !!!

Da questo post apprendo oggi che Ayende ( all'anagrafe come Oren Eini ), uno dei blogger più attivi, e sicuramente il miglior programmatore che abbia mai visto, lascia la sua attuale attività lavorativa alla We! per cercare qualcosa di più entusiasmante da fare. Per chi non lo conoscesse faccio un elenco dei progetti in cui mi è noto sia coinvolto:

  • Rhino Mocs - una libreria per mocking con fluent interface
  • NHibernate Analyzer - un tool per eseguire query con NHibernate
  • Brail - ormai  ufficialmente parte di Castle MonoRail è un view engine che utilizza Boo come linguaggio. 
  • Boo - Uno scripting / compiler per CLI pre rendere facilmente "scriptabili" le proprie applicazioni.
  • Castle Project - IoC container, ActiveRecord con NHibernate, MVC per web con MonoRail...
  • NHIbernate - Il più usato OR/M per .NET.

Ho evidenziato in grassetto i progetti che sono di sua creazione, mentre gli altri sono quelli cui contibuisce, ovviamente in modo incisivo.

 

personalmente se avessi una società mia sarebbe la prima persona che vorrei in squadra.

 

Piccola nota a margine: Ayende è basato in Israele, per cui per lavorare in Europa necessità di un visto, e visto che non ha un "degree" ma è solo un genio totale, la cosa potrebbe pure dargli dei problemi :(

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by felice on Thursday, November 01, 2007 3:25 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Deblector is now Open Source

I managed to keep deblector an open source project. Is hosted now on Codeplex here.

The current release solves the problem with the latest version of Reflector.

Unfortunately there is still some bug, but the product is usable, testable, and I hope that it will grow with other people help. 

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Posted by Felice on Monday, September 03, 2007 11:15 AM
Permalink | Comments (4) | Post RSSRSS comment feed

Castle ActiveRecord + NHibernate 1.2

La versione ufficiale di Castle ActiveRecord non supporta NHibernate 1.2. Per poter utilizzare active record bisogna scaricare il trunk di castle, ma a me il link svn pubblicato per qualche motivo non funziona. In alternativa il trunk si può scaricare dal build server disponibile in http.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Posted by Felice on Wednesday, August 08, 2007 10:44 AM
Permalink | Comments (0) | Post RSSRSS comment feed

NHibernate Paged IBindingList

If you want to bind a big Hibernate query result to a DataGridView in winform, you probably would like to use the SetFirstResult-SetMaxResults features avaiable in IQuery or ICriteria to achieve some kind of "paging". In winform you can chieve this by using virtual mode, but in this case you cannot deal with a DataBound grid, and the presentation code is generally worst. Another problem with paging strategy is sorting the results set. To solve this I created a very simple (abstract ) class that act as a sortable DataSource and transparently drives hibernate to fetch the records in pages. See below the class diagram:

 

 

As you can see, to use the class you must derive one , that must implement:

  • GetTotalCount : returns the whole recordset count based on the objects you need to show
  • GetFillCriteria: returns a ICriteria based on your requirement.

following a sample implementation class:

   12 public class JobPagedDataSource:NHibernatePagedDataSource

   13     {

   14 

   15         public JobPagedDataSource(int pageSize,ISessionFactory factory)

   16             :base(pageSize,factory)

   17         {

   18 

   19         }

   20         protected override int GetTotalCount(ISessionFactory factory)

   21         {

   22             ISession session = Factory.SessionFactory.OpenSession();

   23             IEnumerable cntEn = session.CreateQuery("select count(*) From ....").Enumerable();

   24             IEnumerator en = cntEn.GetEnumerator();

   25             en.MoveNext();

   26             session.Close();

   27             return (int)(long)en.Current;

   28         }

   29 

   30         protected override ICriteria GetFillCriteria(ISession session

   31                                                     , PropertyDescriptor sortProperty

   32                                                     , ListSortDirection direction)

   33         {

   34             ICriteria crit = session.CreateCriteria(typeof(.....));

   35             if (sortProperty != null)

   36             {

   37                 if( direction == ListSortDirection.Ascending )

   38                     crit.AddOrder( Order.Asc(sortProperty.Name));

   39                 else

   40                     crit.AddOrder( Order.Desc(sortProperty.Name));

   41             }

   42             return crit;

   43         }

   44     }

 

If your object does not exactly fit what you need to present to the user, you will probably override the function ApplyObjectDecorator, taking as input the original "hibernated" object and returning your presenter. In this case you would probably do more check in the GetFillCriteria, to properly set the filtering properties.

So, how to attach this to a DataGridView ? Simply do something like:

 

MyDataGridView.DataSource = new MyImplNHibernatePagedDataSource(50,MySessionFactory);

 

and the grid is ready to presents and sort, using a Page Size of 50 objects. For lazy objects you can experience some problem, just because the session will be closed before the datagrid use the objects, and you will have some proxy exception. There is no way out to deal with this, use eager fetch, or avoid to display the lazy columns.

 

 

nhibernatepageddatasource.cs (6,34 kb)

nhibernatepageddatasource.zip (1,49 kb)

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Felice on Thursday, August 02, 2007 2:18 PM
Permalink | Comments (3) | Post RSSRSS comment feed

Cambiare il comportamento di ToString con enum

E' un po' che uso questa strategia, soprattutto perchè mi piace il componente propertygrid, che permette di editare le proprietà pubbliche di un oggetto.  Se una delle proprietà è un enum, viene graziosamente presentato un combobox per scegliere il valore possibile. Tuttavia le scritte presentate sono i nomi interni dell'enum, che possono essere un po' criptici per l'utente finale, e peggio a volte devono essere tradotti. Visto che non si può derivare una classe da un enum per fare l'override di ToString(), una ( possibile ) soluzione è quella di utilizzare un TypeConverter

xes: 

    [TypeConverter(typeof(SheetForecastingEnumConverter))]
    public enum SheetForecastingEnum
    {
        None,
        Auto,
        UserSelection
    }

 

quindi si implementa la classe del converter: 

 

class SheetForecastingEnumConverter : TypeConverter
    {
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string)
            {
                switch (value)
                {
                    case "None":
                        return SheetForecastingEnum.None;
                   ........ // altri case per i vari valori
                }
            }
            return base.ConvertFrom(context, culture, value);
        }
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            //show combobox
            return true;
        }
        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            //show non editable combobox
            return true;
        }
        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            return new StandardValuesCollection
                (
                    new SheetForecastingEnum[] { SheetForecastingEnum.Auto, SheetForecastingEnum.None, SheetForecastingEnum.UserSelection }
                );
        }
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string) && value != null)
            {

                SheetForecastingEnum ct = (SheetForecastingEnum)value;
                switch (ct)
                {
                    case SheetForecastingEnum.None:
                        return "Nessuno";

                    ......
                  
                    default:
                        return "?";

                }
            }
            else
                return base.ConvertTo(context, culture, value, destinationType);
        }
    }

ed il gioco è fatto. 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Posted by Felice on Tuesday, July 31, 2007 5:58 PM
Permalink | Comments (0) | Post RSSRSS comment feed