UI Atoms 1.7.5 Released

On April 3, 2011, in C#, Programming, RIA, Technology, by Akash Kava

We are happy to announce new release of UI Atoms 1.7.5 with following new features.

  1. New AtomDataForm Control
  2. Tabs in AtomForm and AtomDataForm
  3. Lambda Binding Extensions

Introducing new Control AtomDataForm

AtomForm did support UI Validations, however Microsoft RIA Services Client has some inbuilt validation and support for IEditableObject. So we created a new AtomDataForm that supports RIA Services validation and IEditableObject support.

  1. AtomDataForm supports object with IEditableObject interface and fires event for BeginEdit, CancelEdit and EndEdit
  2. AtomDataForm displays items in read only mode unless Edit button is clicked
  3. After edit button is clicked, Save and Cancel button appear for you to persist changes or cancel changes
  4. AtomDataForm contains UI Element (TextBox, ComboBox etc) as children, so you can easily define child ui element in your xaml as shown in sample below
  5. AtomDataForm supports Tabbed layout, for that you can insert your items within AtomDataFormTab as shown in example below
  6. Following sample illustrates Tabs, but you can also create simple user interface without tab as well
  7. You can also use AtomDataFormGroup to group items in to a headered group
  8. Every child element of AtomDataForm can be accessed in code behind file easily because they appear as a variables when you define x:Name property
  9. AtomDataForm supports CanChangeDataContext property, which is false when the form is in edit mode
  10. You can easily reuse any third party control within AtomDataForm, AtomDataFormGroup and AtomDataFormTab
AtomDataForm Sample
  1. <ns:AtomDataForm
  2.     Grid.Column="1"
  3.     DataContext="{Binding SelectedItem,ElementName=dataGrid}" >
  4.             
  5.     <!– 1st Tab–>
  6.     <ns:AtomDataFormTab Header="Default">
  7.                 
  8.         <TextBox
  9.             ns:AtomDataForm.Label="Name:"
  10.             Text="{Binding ProductName, Mode=TwoWay}"/>
  11.                 
  12.         <ns:AtomToggleButtonBar
  13.             ns:AtomDataForm.Label="Type:"
  14.             SelectedItem="{Binding ProductType,Mode=TwoWay}">
  15.             <sys:String>Product</sys:String>
  16.             <sys:String>Service</sys:String>
  17.         </ns:AtomToggleButtonBar>
  18.                 
  19.         <TextBox
  20.             ns:AtomDataForm.Label="Folder:">
  21.             <ns:AtomDataForm.CommandBox>
  22.                 <Button Content="…"/>
  23.             </ns:AtomDataForm.CommandBox>
  24.         </TextBox>
  25.                 
  26.     </ns:AtomDataFormTab>
  27.             
  28.     <!– 2nd Tab–>
  29.     <ns:AtomDataFormTab Header="General">
  30.                 
  31.         <TextBox
  32.             ns:AtomDataForm.Label="Email:"  
  33.             Text="{Binding Email, Mode=TwoWay}"/>
  34.                 
  35.     </ns:AtomDataFormTab>
  36.             
  37.     <!– 3rd Conditional Tab–>
  38.     <!– This tab will be visible only if Product's
  39.     IsTypeService is true–>
  40.     <ns:AtomDataFormTab
  41.         Header="Service"
  42.         IsEnabled="{Binding IsTypeService}">
  43.                 
  44.         <TextBox
  45.             ns:AtomDataForm.Label="Service Details:"/>
  46.                 
  47.     </ns:AtomDataFormTab>
  48.  
  49.     <!– 4th Conditional Tab–>
  50.     <!– This tab will be visible only if Product's
  51.     IsTypeProduct is true–>
  52.     <ns:AtomDataFormTab
  53.         Header="Product"
  54.         IsEnabled="{Binding IsTypeProduct}">
  55.                 
  56.         <TextBox ns:AtomDataForm.Label="Product Details:" >
  57.             <ns:AtomDataForm.CommandBox>
  58.                 <Button Content="Search"/>
  59.             </ns:AtomDataForm.CommandBox>
  60.         </TextBox>
  61.                 
  62.     </ns:AtomDataFormTab>
  63.             
  64. </ns:AtomDataForm>

In above sample you can notice following things,

  1. Header property of AtomDataFormTab is displayed in the title section on the top
  2. AtomDataFormTab contains children and each child can have properties as below
    1. ns:AtomDataForm.Label displays label on left side
    2. ns:AtomDataForm.Description displays description on bottom
    3. ns:AtomDataForm.Title displays title on the top of control
    4. ns:AtomDataForm.CommandBox displays a UI Element on the right corner, usually a search button or expand button
  3. AtomDataFormTab supports IsEnabled binding, you can bind this property to show/hide the tab as shown in the example above
  4. Every child elemen of either AtomDataForm , AtomDataFormTab or AtomDataFormGroup supports Visibility and IsEnabled binding
    1. If Visibility is bound and it results in Collapsed then entire form item is not displayed
    2. IsEnabled binding can enable/disable the editable control

Following is screenshot of Tabbed AtomDataForm in edit mode

AtomDataForm

Introducing Lambda Binder Extensions

Writing binding expressions with conditions and converters can be very complex especially rewriting many and same logic at different classes. Now UI Atoms support, Lambda Binding Extensions which lets you do binding within the code without creating any complex IValueConverter implementation.

Bind Method Extension

Element Property Binding
  1. theForm.Bind(AtomDataForm.HeaderProperty,
  2.     () => string.Format("{0} ({1})",
  3.             productName.Text,
  4.             typeToggleButtonBar.SelectedItem
  5.         ));

This will bind theForm’s Header property to an expression that will combine properties of two different elements. And this will also automatically update when any of bound source or its property will change.

DataContext Property Binding
  1. theForm.Bind(AtomDataForm.HeaderProperty,
  2.     () => string.Format("{0} ({1})",
  3.             theForm.DataContext.Property("ProductName"),
  4.             theForm.DataContext.Property("ProductType")
  5.         ));

Assuming, we may not have property information at design time, but we know that DataContext will be set to an object containing properties, then we can write Property extension method as shown above.

BindVisibility Method Extension

Visibility converters are very frequent so we created a BindVisibility extension method that will let you specify a boolean expression that will be converted to Visibility on the fly.

BindVisibility Extension
  1. productTab.BindVisibility(
  2.     () =>
  3.         typeToggleButtonBar.SelectedIndex == 0);

These extensions can be used anywhere in any third party controls as well.

Download Now

Click here to download your free demo copy of NeuroSpeech UI Atoms 1.7.5

Share
Tagged with:  

Lambda Binder for WPF and Silverlight

On March 20, 2011, in C#, Technology, by Akash Kava

LambdaBinder class helps in creating complex binding expressions without having to create IValueConverter and IMultiValueConverter, it lets you write inline expresions with advanced binding scenarios.

Lets say, you have two TextBox for first and last name and you want a Title to be displayed as addition of both.

So lets say, you can write it as follow,

Adding Two Properties
title.Bind(TextBlock.TextProperty,
    () => firstName.Text + " " + lastName.Text);

Formatting Properties
title.Bind(TextBlock.TextProperty,
    () => string.Format("{0} {1}",
        firstName.Text, lastName.Text));

You can pass on the Linq expression to Bind extension method of LamdaBinder and that will convert and set binding so that anytime if firstName or lastName is modified, the title will be updated automatically.

Assuming I want some hypothetical visibility logic, for example, only if the firstName is entered, lastName should be visible.

Conditional Visibility Binding
lastName.Bind(TextBox.VisibilityProperty,
    () => firstName.Text.Length > 0 ? Visibility.Visible :
        Visibility.Collapsed);

You can also use this to avoid writing IValueConverters and do inline binding in code very easily.

Lambda Binder
public static class LambdaBinder
{

    public static void Bind(
        this DependencyObject dest,
        DependencyProperty destProperty,
        Expression<Func<object>> func)
    {
        MultiBinding b = new MultiBinding();
        b.Converter = new MultiDelegateConverter(func.Compile());
        LambdaBindingBuilder bb = new LambdaBindingBuilder(b);
        bb.Visit(func.Body);
        BindingOperations.SetBinding(dest, destProperty, b);
    }

}

public class LambdaBindingBuilder : ExpressionVisitor
{
    private MultiBinding multiBinding;

    private string lastPath = null;

    public LambdaBindingBuilder(MultiBinding mb)
    {
        this.multiBinding = mb;
    }

    protected override System.Linq.Expressions.Expression VisitMember(MemberExpression node)
    {
        PropertyInfo p = node.Member as PropertyInfo;
        if (p != null) {
            if (string.IsNullOrWhiteSpace(lastPath))
            {
                lastPath = p.Name;
            }
            else {
                lastPath = p.Name + "." + lastPath;
            }
        }
        FieldInfo f = node.Member as FieldInfo;
        if (f != null) {
            Binding b = new Binding(lastPath);
            b.Source = System.Linq.Expressions.Expression.Lambda<Func<object>>(node).Compile()();
            multiBinding.Bindings.Add(b);
            lastPath = null;
        }
        return base.VisitMember(node);
    }

    protected override System.Linq.Expressions.Expression VisitConstant(ConstantExpression node)
    {
        if (!string.IsNullOrWhiteSpace(lastPath)) {
            Binding b = new Binding(lastPath);
            b.Source = node.Value;
            multiBinding.Bindings.Add(b);
            lastPath = null;
        }
        return base.VisitConstant(node);
    }
}

public class MultiDelegateConverter : IMultiValueConverter
{
    private Func<object> func;

    public MultiDelegateConverter(Func<object> func)
    {
        this.func = func;
    }

    public object Convert(object[] values,
        Type targetType,
        object parameter,
        System.Globalization.CultureInfo culture)
    {
        return func();
    }

    public object[] ConvertBack(object value,
        Type[] targetTypes,
        object parameter,
        System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

 

LambdaBinder is part of UI Atoms suite. At NeuroSpeech, we do research on UI and we are coming up with more interesting featues in UI Atoms.

Please Click Here to download latest update of UI Atoms with AtomForm including Tabs in the form.

Share
Tagged with:  

Entity Framework and RIA Services do not allow Entities to be added/removed from cross containers. In order to move Entity from one DomainContext/ObjectContext to another DomainContext/ObjectContext, we need to recursively detach the object graph and attach it to other DomainContext/ObjectContext.

Object Graph

Single entity can be easily detached and attached from EntitySet where it belongs, but the problem comes when navigation properties of entity are not empty. Entity object along with navigation properties is called Object Graph because if you notice, navigation property’s navigation property will contain reference to same entity and that will result in endless recursive code for Detach/Attach.

An entity with the same identity already exists in the EntitySet

When you try to detach entity from old DomainContext/ObjectContext and attach it to DomainContext/ObjectContext, it may give you an error that entity with same identity already exists and it will throw an exception. In this case we will just simply reuse the existing entity instead of attaching the entity we have.

Entity cannot be attached to this EntityContainer because it is already attached to another EntityContainer

In case of ObjectGraph, your root level entity is already detached, but navigation properties are not detached, and while you try to attach your root level entity, it will throw same error for entities that exist in navigation properties.  Because detach method does not recursively detach every entity from navigation properties.

Attach/Detach Extension Methods

Finally after brainstorming little, I made following class that will allow you to recursively detach/attach object graphs from DomainContext. You can replace DomainContext to ObjectContext to use it inside Entity Framework.

DomainContext Extensions
  1. /// <summary>
  2. /// DomainContext Extensions
  3. /// </summary>
  4. public static class DomainContextExtensions {
  5.  
  6.     /// <summary>
  7.     /// Recursively Attaches entity loaded from Other DomainContext to
  8.     /// current specified DomainContext
  9.     /// </summary>
  10.     /// <param name="context">DomainContext where entity will be attached</param>
  11.     /// <param name="entity">Entity loaded from other DomainContext</param>
  12.     /// <returns></returns>
  13.     public static Entity Attach(this DomainContext context, Entity entity)
  14.     {
  15.         if (entity == null || entity.EntityState != EntityState.Detached)
  16.             return entity;
  17.  
  18.         Entity newEntity = entity;
  19.  
  20.         Entity[] list = new Entity[] { entity };
  21.         foreach (Entity c in context.EntityContainer.LoadEntities(list,
  22.             LoadBehavior.MergeIntoCurrent))
  23.         {
  24.             newEntity = c;
  25.             break;
  26.         }
  27.  
  28.         // recursively attach all entities..
  29.         Type entityType = typeof(Entity);
  30.  
  31.         // get all navigation properties…
  32.         Type type = entity.GetType();
  33.         foreach (var item in type.GetProperties())
  34.         {
  35.             if (entityType.IsAssignableFrom(item.PropertyType))
  36.             {
  37.                 Entity navEntity = Attach(context, item.GetValue(entity, null)
  38.                     as Entity);
  39.                 item.SetValue(newEntity,navEntity, null);
  40.                 continue;
  41.             }
  42.             if (item.PropertyType.Name.StartsWith("EntityCollection"))
  43.             {
  44.                 IEnumerable coll = item.GetValue(entity, null) as IEnumerable;
  45.                 List<Entity> newList = new List<Entity>();
  46.                 foreach (Entity child in coll)
  47.                 {
  48.                     newList.Add(Attach(context, child));
  49.                 }
  50.                 dynamic dcoll = item.GetValue(newEntity,null);
  51.                 foreach (dynamic child in newList)
  52.                 {
  53.                     dcoll.Add(child);
  54.                 }
  55.             }
  56.         }
  57.         return newEntity;
  58.     }
  59.  
  60.     /// <summary>
  61.     /// Recursively detaches entities from DomainContext, this
  62.     /// method detaches every navigation properties
  63.     /// of current Entity as well.
  64.     /// </summary>
  65.     /// <param name="context"></param>
  66.     /// <param name="entity"></param>
  67.     public static void Detach(this DomainContext context, Entity entity)
  68.     {
  69.         if (entity == null || entity.EntityState == EntityState.Detached)
  70.             return;
  71.         EntitySet nes = context.EntityContainer.GetEntitySet(entity.GetType());
  72.         nes.Detach(entity);
  73.  
  74.         Type entityType = typeof(Entity);
  75.  
  76.         // get all navigation properties…
  77.         Type type = entity.GetType();
  78.         foreach (var item in type.GetProperties())
  79.         {
  80.             if (entityType.IsAssignableFrom(item.PropertyType))
  81.             {
  82.                 Detach(context,item.GetValue(entity, null) as Entity);
  83.                 continue;
  84.             }
  85.             if (item.PropertyType.Name.StartsWith("EntityCollection"))
  86.             {
  87.                 IEnumerable coll = item.GetValue(entity, null) as IEnumerable;
  88.                 foreach (Entity child in coll)
  89.                 {
  90.                     Detach(context,child);
  91.                 }
  92.             }
  93.         }
  94.     }
  95.  
  96. }

Share
Tagged with:  

UI Atoms 1.5.8 Released

On January 11, 2011, in Programming, by Akash Kava

We are happy to announce new release of UI Atoms 1.5.8 with following new features.

UI Atoms for WPF 4.0

In this release of UI Atoms, we have a new library of dlls specially designed for WPF 4.0, which now includes support for Visual State Managers and renders everything in WPF 4.0 presentation. You can find Bin.Net4 folder inside WPF, that contains libraries only for WPF 4.0 version.

AtomForm Label Alignment for Silverlight and WPF

Silverlight-Form-Label-Alignment

AtomForm now provides following two properties,

  • LabelHorizontalAlignment property that can align the label horizontally in the form item.
  • LabelVerticalAlignment property that can align the label vertically in the form item.

Label Can now contain string or UI Element object

Rich Label Format
  1. <ns:AtomTextBox HorizontalAlignment="Stretch"
  2.                 Text="{Binding Path=ProjectName, Mode=TwoWay}" Width="Auto" >
  3.     <ns:AtomForm.Label>
  4.         <StackPanel Orientation="Horizontal">
  5.             <TextBlock>Name:</TextBlock>
  6.             <Image Source="/PrismTest;component/Images/Logo.png" Width="16" Height="16" />
  7.         </StackPanel>
  8.     </ns:AtomForm.Label>
  9. </ns:AtomTextBox>

Rich-Label-Silverlight-WPF-Form

 

Advanced Quick Form Designer

Typing Tags with all Bindings could be quite time consuming, where it involves moving items up and down, deleting and adding new Form Items. Advanced Quick Form Designer resolves all this problems by giving you state of art designer, which provides one quick grid to enter all necessary information. However the form designer works well even for existing forms, and you can generate entire new form by selecting some Type as well.

Advanced-Silverlight-WPF-Form-Designer

As you see, type of control can be changed that will represent the form item and corresponding binding property can be changed as well. Just with simple one click, you can move items up and down or delete them. You can add a new control that is defaulted to AtomTextBox at the top, you can change default control to add multiple one after another very easily. Also you can click “Generate From Type” to select any Business Object Type and generate a brand new form.

Generate-Form-From-Type-Silverlight-WPF

Selecting type will automatically generate form with its bindings.

Share
Tagged with:  

Enabling Tracing in Silverlight with UI Atoms

On June 19, 2010, in C#, by Akash Kava

UI Atoms 2010 v1.9 update now includes Trace API for developers to log information on screen just like normal .NET application.

Diagnostics Trace API

Microsoft .NET provided Trace API to help in troubleshoot problems which is of great help at time of development. But Silverlight developers are disappointed for not being able to trace in Silverlight apps. However running apps on multiple platform isn’t quite easy, and different user experiences on different platforms raise unexpected issues. Such issues are difficult to resolve as deployed client computer may not have development tools available to troubleshoot and see application logs.

Trace.WriteLine(“ Hello World “);

We sure miss Trace in Silverlight, so we decided to add similar diagnostics API in Silverlight.

Trace Viewer

In Silverlight, as it runs inside a browser, there is no way to see any trace information not only this, on different platforms like Mac, it may be too difficult for no technical user to install and enable remote debugging.

So we decided to include a visual component that can be put anywhere on screen, just like “Output” pane in visual studio. Its developer’s choice to fit the Trace Viewer. Its pretty simple Text View control, where in you can see lines of log automatically scrolling down as log grows.

AtomTraceView

UI Atoms’s AtomTraceView control works well with Silverlight as well as WPF, in WPF it uses inbuilt Trace functionality and displays trace information to user, and in Silverlight we have added custom API, that is used to display Trace information. Syntax is pretty simple.

<Grid>
   <Grid.RowDefintions>
       <RowDefinition/>
       <RowDefinition Height="200"/>
   <Grid.RowDefintions>

   <MyUserControl/>

   <AtomTraceView Grid.Row="1"/>

<Grid>

This way you can include AtomTraceView at the bottom of your page and it will display all traces programmed by the developer.

Limited API

However, .NET Trace API is very huge, and it is extensible, but Trace API offered by UI Atoms as of now is very limited, not by the design but by implications, Silverlight itself is very limited platform, this is just an Add-on for users to troubleshoot their apps.

Trace Statements in Silverlight

In order to write Trace statements, you have to import following namespace,

using NeuroSpeech.UIAtoms.Core;

Then you can write following statements and they will appear in AtomTraceView Control.

AtomTrace.WriteLine(" Hello World ");

AtomTrace.WriteLine( exception.ToString() );

AtomTrace.WriteLine( string.Format("{0},{1} Coordinates", x, y) );

 

The AtomTrace class exists for both Silverlight and WPF, so it becomes easier to write your logs with AtomTrace instead of Trace, so your code can be shared on both Silverlight as well as WPF.

Trace and Multithreading

Trace statements can be accessed by thread in Silverlight app, however the sequence in which it will appear can not be guaranteed because it depends upon the processor and scheduling algorithm used in thread pool of Silverlight application.

Custom Trace Listener

Just similar to .NET, we have included  AtomTraceListener class, which you can derive and customize trace information according to your need as well as it has Traced event, which you can implement and get trace notifications anywhere..

 

Future of Trace API in Silverlight

When Trace API will be included by Microsoft in future edition of Silverlight, we may plan to remove Trace API, to be consistent with the Silverlight development workflow, this API has been enabled only to write shared code between WPF and Silverlight environment. However otherwise, we plan to make tracing more rich with UI Atoms. But for now, it is very helpful for everyone.

Live Demo

Click Here to see live demo of Tracing in Silverlight.

Click Here to download demo version of UI Atoms 2010.

Share
Tagged with:  

SQL Assembly brings power of CLR (Common language runtime) inside SQL Database Stored Procedure which can overcome T-SQL programming pains of Intellisense, Debugging, Logging and Versioning.

Traditional Problems with T-SQL and Stored Procedures

I never liked programming in T-SQL, for various reasons listed below,

  1. No Intellisense, of course it has been introduced SQL 2008 onwards, but still its far behind what we get in Visual Studio.
  2. Source Code Control, although scripts can be added to source code control, usually its unawareness and unwillingness of Database Administrator / Designer to use it correctly and mess things up.
  3. Less human readable, T-SQL is very difficult to understand if its not documented well.
  4. Source code is open, it can be modified, so in short Database Administrator has complete access to alter business logic.
  5. Very less reusable, unless you do copy paste, no way to standardize the development.
  6. No support for advanced visual tools to design T-SQL Stored Procedures.
  7. Deployment is difficult, minor alternation can cause problems.
  8. Most of business logic involves, loops, if-else, switch statements, and it takes 10 times more code to execute such logic in T-SQL then any other programming language in world.
  9. You need multiple language talent, one with either VB.NET/C# and T-SQL.
  10. Difficult to Log information.

Introduction of SQL Assembly

Since SQL 2005, introduction of SQL Assembly has been of great importance, however not much of people still use it, because of unawareness. But SQL Assembly can be modularized and reused, and it exists as simple CLR assembly inside database.

And following are benefits of SQL Assembly, besides few listed in MSDN here.

  1. LINQ in CLR Stored Procedure.
  2. Better Source Code control over Visual C#/Visual Basic.NET project through Team System.
  3. Intellisense.
  4. Closed source, its safe.
  5. Highly secure as Database Administrator can not alter business logic.
  6. C# and VB.NET along with LINQ are more human readable, understanding and reusing code can be very easy.
  7. Support for advanced documentation in comments.
  8. Visual Studio Model designer can be used.
  9. Reuse your Business Logic for other database vendor products, e.g. Oracle, MySQL (Explained below).
  10. You can do advanced logging with log-4-net or custom logging.

Small Example of How to use LINQ in SQL Assembly

  1. Create a simple Visual C# Class Library Project.
  2. Add reference of “System.Data” Assembly.
  3. Create new ADO.NET Entity Model, and you can create it from Database or empty model, the way you want to develop your project.
  4. Add few entities, I am going to demonstrate entity “Cart” which is a saved shopping cart for the Customer.

CartDemoCreate a Stored Procedure Class

After creating model, lets create a class called “ShoppingCart” and add a static method as shown below.

public class ShoppingCart
{

    /// <summary>
    /// Creates new Cart if none exists and returns the Cart ID
    /// </summary>
    /// <param name="CustomerID"></param>
    /// <returns></returns>
    [SqlProcedure]
    public static SqlInt64 GetSavedCartID(SqlInt64 CustomerID)
    {

        // Set context connection=true
        ShoppingCartModelContainer context =
            new ShoppingCartModelContainer("context connection=true");

        // Query for exisitng cart
        Cart c = context.Carts.FirstOrDefault(
                        t => t.CustomerID == CustomerID.Value);

        if (c == null) { 

            // create new Cart
            c = Cart.CreateCart(0, CustomerID.Value, DateTime.Now);
            context.AddToCarts(c);
            context.SaveChanges();
        }

        // return the Cart ID
        return new SqlInt64(c.CartdID);

    }

}

This code demonstrates how easy it is to write SQL Assembly, now here is the ultimate benefit of everything.

Ultimate Benefit

You can use your Business Logic in multiple vendor databases, like for example for Oracle and MySQL, you can reuse your SQL Assembly and just change your connection string. You can reuse your business logic in your front end code with simple wrapper.

And with SQL Server, you can reuse this assembly inside SQL Database.

CodeSharing

Share
Tagged with:  

C# Optimizations Via Caching – Part 2

On March 22, 2009, in C#, Programming, by Akash Kava

I decided to write part 2 of optimization, with C# Project, to actually test the performance for accessing NameValueCollection and Property access.

I had lot of comments by extra smart people quoting regarding my previous post at "C# Optimizations Via Caching" . That all methods posted do not do any significant impact on performance, I wonder people just have habit of posting any thing without even thinking once.

So I decided to write a piece of code to actually demonstrate the optimization impact with statistics. In this Visual Studio 2008 C# Console Project, I wrote a simple code to access an application config value directly for 10,000 times in a loop and then to access it via statically cached variable. And in second part, I implemented accessing property and accessing a local variable nearly 10 million times to detect noticeable effect.

The tests are as follow,

Lets look at the test code to access config values, also please note that config values are stored in NameValueCollection, which is same object used in Request, Response and Session objects in web request.

// Configuration Access Test
Console.WriteLine("Testing Direct Config Access");

Test(1000000, () => {
     string v = System.Configuration.ConfigurationManager.AppSettings["Key"];
});

Console.WriteLine("Testing Cached Access");
Test(1000000, () => {
     string v = SettingVal;
});

Following is the result, on Intel Centrino Duo with 1GB of RAM, this shows that the code with cached access in static variable is 100 times faster.

Testing Direct Config Access

Time Difference 1125 MilSecs

Testing Cached Access

Time Difference 15.625 MilSecs

So now next time you code, remember it to not access frequently in loops.

Lets talk about property access,

// Property in Loop Test
int pc = 10000000;
Console.WriteLine("Testing Direct Property Access");
Test(() => {
    Person p1 = new Person();
    for (int i = 0; i < pc; i++)
    {
        string v = p1.Name;
    }
});

Console.WriteLine("Testing Local Variable Access");
Test(() => {
    Person p1 = new Person();
    string t = p1.Name;
    for (int i = 0; i < pc; i++)
    {
        string v = t;
    }
});

In order to detect any noticeable difference, we have to loop for about 10 million times, however, in any usual code, you will be accessing 100s of properties, so it may make a good difference when you write code, which executes million times parallel.

Testing Direct Property Access

Time Difference 140.625 MilSecs

Testing Local Variable Access

Time Difference 46.875 MilSecs

Now based on the amount of free memory, and cpu speed, you might have to configure the number of iterations up and down to get a good noticeable effect of two different ways.

I can clearly see the difference, remember on normal piece of code, you will be accessing over 10 to 100s of properties again and again.

Click on Caching Stats Visual Studio Project to download and test by yourself.

Share
Tagged with:  

C# Optimizations Via Caching

On December 12, 2008, in C#, by Akash Kava

Being computer science expert, I can visualize the load on Processor when I write the code, and though its cumbersome sometime to write more code to perform optimized operation,but its real worth while to spend time to understand Caching in programming.

Static Caching Config Values

The configuration values that you probably store in some config file, or some xml, or some registry etc should be statically cached in any class. For example, you need to access "AppConnectionString" defined in web.config or app.config, if you use expression

WebConfigurationManager.ConnectionStrings["AppConnectionString"]

every time then it would be very costly as first it tries to get instance of config file, then the section and then once again the HashTable of probably NamedValueCollection type and so on. This all is very costly for any value that is stored in config file and which stays unchanged for months or even years. I would always access it in a static class like this.

private static string _AppConnectionString = null;
public static string AppConnectionString
{
    if(_AppConnectionString == null)
    {
        _AppConnectionString =
            WebConfigurationManager
             .ConnectionStrings["AppConnectionString"]
             .ConnectionString;
    }
    return _AppConnectionString;
}

Property Cache in Local Variable

Consider following code

void DrawEffectRectangle(Rectangle r)
{
   for(int i=50; i>=0;i--)
   {
      SetColor(i*5);
      Draw3dRect(r.Left - i, r.Top - i, r.Right + i, r.Bottom + i);
   }
}

This method uses 4 properties, Left, Top, Right and Bottom, now consider following effective code which produces exactly same effect.

void DrawEffectRectangle(Rectangle r)
{
   int left = r.Left;
   int top = r.Top;
   int bottom = r.Bottom;
   int right = r.Right;
   for(int i=50; i>=0;i--)
   {
      SetColor(i*5);
      Draw3dRect(left - i, top - i, right + i, bottom + i);
   }
}

Now note, the calls to property evaluation has been reduced, lets assume one property access code takes 1ms then this code will execute 446ms times faster then the earlier code.

Note, you should use this technique carefully after examining that the value of these property evaluations are constant throughout the execution.

Cache Before Loop

Assuming following code,

for(int i=0;i<Results.Length;i++)
{
   ProcessResult(Results[i]);
}

Now here as well, expression i<Results.Length gets evaluated Results.Length times, assuming it to be an array. But consider the following code,

int len = Results.Length;
for(int i=0;i<len;i++)
{
   ProcessResult(Results[i]);
}

Caching once again the Length in a local variable reduces Property Evaluation code.

QueryString Caching

Most of the time, we have habbit of using ‘Request.QueryString["SomeID"]‘ this is very bad if you keep on calling this way, you must cache this in beginning of Page processing that will significantly improve your Web Server Performance. I would use following way to initialize my QueryString based Page variables.

private T GetQueryStringValue<T>(string name, T def)
{
   object val = Request.QueryString[name];
   if(val == null)
      return def;
   return (T)Converter.ChangeType(typeof(T),val);
}

private long CustomerID = 0;
private string Process = "";

private void Page_Load(object sender, EventArgs e)
{
   CustomerID = GetQueryStringValue<long>("CustomerID", 0);
   Process = GetQueryStringValue<string>("Process", "Signup");
}

And then using the values of CustomerID and Process variables rather then accessing Request.QueryString object again and again. There are two advantages in it, first, you can avoid Request.QueryString calls, and second you can compare native type comparisons to evaluate your algorithm flow. And you can see yourself after decompiling Request object through Reflector, that each access of Request.QueryString and its indexer basically requires more then 20-80 steps to get final result. Sure processor has 3 GHz but that doesnt mean that 1000 requests can just waste so many cpu cycles and finally frustrating the end user who is waiting expecting some result !!

Session Variable Caching

I would also try to cache, session variable in local page class as defined following,

private long _SessionWebUserID = -1;
private long SessionWebUserID{
   get{
      if(_SessionWebUserID == -1)
      {
         _SessionWebUserID = 0;
         object val = Session["WebUserID"];
         if(val != null)
         {
            _SessionWebUserID = (long)val;
         }
      }
      return _SessionWebUserID;
   }
}

Sure this method has additional _SessionWebUserID != -1 comparison everytime you access the property, but belive me, its far less costly then accessing Session NamedValueCollection again and again.

However the drawback is, if you cache session variable for longer period then its life in static variable, if it changes outside in session, you will never know. Thats why I always keep it as instance property instead of static, so I assume that atleast for this instance of page, the session value is constant.

Avoid Property of Property Style

Consider following code,

string url = HttpContext.Current.Request.Url;
string method = HttpContext.Current.Request.Method;

Both method has HttpContext.Current.Request as common code, and it actually has 2 Property Evaluation codes. Considering simple property evaluation execution time is 1ms, above code finishes in 6ms.

Ideally such code should be written as,

HttpRequest request = HttpContext.Current.Request;
string url = request.Url;
string method = request.Method;

Now this rewritten code finishes in 4ms, as first statement executes two property evaluation code to get final useful HttpRequest object.

Caching Method Results

Sure all of above methods even apply to method results too, as Property evaluation is just an internal get_Property method.

Result

Ok fine, I do the caching but how it improves performance? The answer is quite simple, each of above discussed methods requires numerous Property evaluation, Hashtable eveluation code, in order to read certain values. Now accessing local stack variable or static variable is many times faster then accessing property as it is a new method to access some other variable or some calculated variable. The indexed or hashed properties take even more time as the search is dependent upon the number of items present in the list or dictionary. Each such operation may execute few millsecond faster then before, but the overall performance increases with many concurrent execution. Not only CPU cycles but memory usage also can be improved as less method calls, less stack in use and less overhead on memory.

Oh such big lecture just for avoiding Property Evaluations? Well yes, remember Property Evaluation code is nothing but a simple method execution for CPU. And inside the .NET CLR environment, CPU needs to do a lot before an actual code is executed. Please note, this is assumed hypothetical Call sequence, the actual .NET CLR call sequence can be much more complex.

  1. Access target object (in which property exists)
  2. Access internal "Get" method handle for this target object’s type
  3. Allocate the Stack
  4. Do some book keeping, note the pointer values for tracking Exception StackTrace
  5. Setup local variables if any
  6. Move to the pointer of first instruction
  7. Evaluate expressions and actual code (for NamedValueCollection this could be quite long)
  8. Push result on stack
  9. Finish the Call
  10. Pop the result
  11. Unwind stack and do some book keeping to unwind Exception StackTrace

So, you can see, CPU needs to do a lot just before it can give you property evaluation result. And in case of Cached value in local variable, this can simply reduce in one or two instructions for subsequent frequent access.

Memory Usage

Using more such cached variables doesnt increase memory overhead? Well significantly not, because you are not going to have million such variables in one single threaded execution, most of these variables will be reused from time to time. Each local cached variable in method execution is on stack, and it only takes one space unit, however executing actual property again and again will even require more stack usage.

This isn’t same as External Caching

Sure web servers are written with great deal of effort and do provide good caching mechanism. Even ASP.NET has various caching methods, but those caching things are for very generalized purpose, the methods I am detecting here is not supposed to be misunderstood with the page, control caching. Because even other caching methods requires at least first time complete processing in order to get the results. Also these are pure processor level execution improvements, it has nothing to do with where your application is executing, it can be your web server code or your mobile app code or even database assembly code.

Share
Tagged with: