ObjectCache – Caching

In the ASP.NET domain all the state mechanisms can be considered as caching, both in the client side (view states, query strings, cookies) and in the server side (application state, session state and the Cache object itself.) You can define the classes and properties as static to get the effective functionality of caching. In ASP.NET the Cache object is HttpContext.Cache and .NET 4 introduced the ObjectCache to be used in non ASP.NET applications. This post will walk you through about the ObjectCache.    Learn about Windows Azure Caching.

ObjectCache

This is included in the System.Runtime.Caching assembly. MemoryCache is the concrete implementation of this library. The following method provides a quick glance on how ObjectCache can be used.

   1: public static void PutInCache(string key, object value)

   2: {

   3:     

   4:     var cache = MemoryCache.Default;

   5:     var policy = new CacheItemPolicy()

   6:     {

   7:         AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(5)),

   8:         Priority = CacheItemPriority.Default

   9:     };

  10:  

  11:     Console.WriteLine("Cache Size for the application in MB - {0}", cache.CacheMemoryLimit / ( 1024 * 1024) );

  12:     Console.WriteLine("{0}% cache memory is used.", (100 - cache.PhysicalMemoryLimit));

  13:  

  14:     cache.Remove(key);

  15:     

  16:     cache.Add(key, value, policy);

  17:  

  18:     int result = (int) cache.Get(key);

  19:     

  20: }

CacheMemoryLimit property gives the allocated cache memory for the specific instance of your application, where as PhysicalMemoryLimit give the unused space of the cache in percentage. When the cache memory  reaches beyond the CacheMemoryLimit then cache values are removed automatically, we can track this and take actions by registering a callback for the cache item removal.

Cache have 2 types of expiration policies. AbsoluteExpiration is the definite time after which there’s no guarantee for the item to be available in the cache memory, SlidingExpiration is where if there’re no access to the particular cache item within the specified time period there’s no guarantee for that item to be available. These 2 are very common and available in the HttpContext.Cache as well.

Cache has a priority which takes the value of the CachItemPriority enum. This enum has 2 values Default and Not Removable. Default is the default set up, means when you do not mention any value this would be applied. Default ensures the default behavior of the cache. Not Removable is used to instruct the system not to clear the values from the cache even when the system runs low in memory. These values should be cleared explicitly.

Windows server 2008 Core doesn’t support the ObjectCache and some Itanium implementations also do not support ObjectCache. Windows Server 2008 R2 with SP1 and later versions (including Windows 8.1) support ObjectCache.

Advertisement

Few good to know features of Entity Framework

I have been doing some reading about advance EF features for last couple of weeks and got to know some interesting things, this post is a compilation of the information and provides links to the relevant sites.

Starting with EF 5.0 ObjectContext was replaced with DbContext. So what’s the difference between ObjectContext and DbContext ?

This link gives the simplest answer for the above question. And using the following code you get the ObjectContext from your DbContext. Say that your have model class named MyEntities the best practice is to create a partial class (partial classes are the safest way since the auto generated classes could be overridden at any time there’s a code regeneration) and create a property that returns the ObjectContext. The below code describes the idea.

   1: public partial class MyEntities : DbContext

   2: {

   3:     public System.Data.Entity.Core.Objects.ObjectContext ObjectContextProperty

   4:     {

   5:         get

   6:         {

   7:             return ((IObjectContextAdapter)this).ObjectContext;

   8:         }

   9:     }

  10: }

 

Compiled Queries

Compiled Queries are helpful when we want to squeeze the final drop of performance from our application. We cache the TSQL statement which is produced from LINQ to Entity queries we write. Since we write the queries in LINQ these queries first get translated into TSQL statements and they’re executed against the SQL Server. As a result of caching the TSQL statements of the LINQ queries we cut down the translation time. Microsoft has mentioned this provides 7% performance improvement. Details here

This article provides information on creating compiled queries

MSDN Compile Query Class

The reason why I haven’t mention any samples here is that we do not need to worry much about the query compilation in EF 5.0 and above because it automatically happens.

This MSDN blog post explains the automatic query compilation in EF 5.0 and above. But again if you want to have more advanced and granular control over the compilation you should consider implementing your custom compiled queries. It’s better to have the compiled queries in your data repository classes.

Performance

Read this Data Access Blog article about the EF 5.0 performance. The below image take from the blog gives a summary.

image 

As you see that updating to EF 5.0 on .NET 4.5 gives a greater performance boost to LINQ to Entities, automatic query compilation is one of the main features for this performance gain.

EF Versions – http://msdn.microsoft.com/en-us/data/jj574253

This channel 9 video is a good one about EF 5 and EF 6

 http://channel9.msdn.com/Shows/Visual-Studio-Toolbox/Entity-Framework-5-and-6

Difference between Single and First in C#

In the extension methods Single / SingleOrDefault and First / FirstOrDefault have semantic distinction. Though in many places these methods are being used without the semantic consideration, more often they return the expected results. This behavior makes this misuse more common.

Both the Single and First methods have an accompanying helper method named as xxOrDefault which does nothing more than returning the default value of the specified data type if no element matches the query in the collection. For example if it’s a string collection they return null and if it’s an integer collection they return 0.

So the main semantic distinction comes between Single and First

To check this we create 3 collections like the following.

   1: private static List<StudentViewModel> list0;

   2:  

   3: private static List<StudentViewModel> list1 = new List<StudentViewModel>()

   4: {

   5:     new StudentViewModel() { StudentId = 1, StudentName = "Thuru", Gpa = 3.92},

   6:     new StudentViewModel() { StudentId = 2, StudentName = "Kristin", Gpa = 3.92},

   7:     new StudentViewModel() { StudentId = 3, StudentName = "Jack", Gpa = 3.92},

   8:     new StudentViewModel() { StudentId = 4, StudentName = "Anna", Gpa = 3.92},

   9:     new StudentViewModel() { StudentId = 5, StudentName = "Anderson", Gpa = 3.92},

  10:     new StudentViewModel() { StudentId = 6, StudentName = "Niki", Gpa = 3.92},

  11:     new StudentViewModel() { StudentId = 7, StudentName = "Jhon", Gpa = 3.92},

  12:     new StudentViewModel() { StudentId = 7, StudentName = "Brown", Gpa = 3.92},

  13: };

  14:  

  15: private static List<StudentViewModel> list2 = new List<StudentViewModel>()

  16: {

  17:     

  18: };

list0 is null, list1 and list2 are instantiated collections where list2 has no elements.

 

Single / SingleOrDefault

Single as the name specifies is used and should be used where there’s one and only element match the query.

   1: //ArgumentNullException

   2: list0.Single();

   3:  

   4: //ArgumentNullException

   5: list0.SingleOrDefault();

   6:  

   7: // InvalidOperationException - Sequence contains no elements

   8: list2.Single();

   9:  

  10: // works return null if there's no elements matching the criteria

  11: StudentViewModel st = list2.SingleOrDefault();

  12:  

  13:  

  14: // InvalidOperationException - Sequence contains more than one element

  15: // if collection has only one element it returns that element

  16: Console.WriteLine(list1.Single());

  17:  

  18: // works

  19: Console.WriteLine(list1.Single(e => e.StudentId == 1));

  20:  

  21: // InvalidOperationException - Sequence contains more than one matching element

  22: Console.WriteLine(list1.Single(e => e.StudentId == 7));

  23:  

  24: // InvalidOperationException - Sequence contains no matching element

  25: Console.WriteLine(list1.Single(e => e.StudentId == 8));

  26:  

  27: StudentViewModel st1;

  28:  

  29: // InvalidOperationException - Sequence contains more than one element

  30: st1 = list1.SingleOrDefault();

  31:  

  32: //works

  33: st1 = list1.SingleOrDefault(e => e.StudentId == 1);

  34:  

  35: // InvalidOperationException - Sequence contains more than one matching element

  36: st1 = list1.SingleOrDefault(e => e.StudentId == 7);

  37:  

  38: // works st1 is null

  39: st1 = list1.SingleOrDefault(e => e.StudentId == 8);

 

First / FirstOrDefault

First has no issues when there’re more than one element matches the query, it simply returns the first element. But if there’re no element matching the query it prompts.

   1: // ArgumentNullException

   2: list0.First();

   3:  

   4: // ArgumentNullException

   5: list0.FirstOrDefault();

   6:  

   7: // InvalidOperationException - Sequence contains no elements

   8: list2.First();

   9:  

  10: // works return null if there's no elements matching the criteria

  11: StudentViewModel st = list2.FirstOrDefault();

  12:  

  13:  

  14: // returns the first element of the collection

  15: Console.WriteLine(list1.First());

  16:  

  17: // works

  18: Console.WriteLine(list1.First(e => e.StudentId == 1));

  19:  

  20: // returns the first element of the collection matches the query

  21: Console.WriteLine(list1.First(e => e.StudentId == 7));

  22:  

  23: // InvalidOperationException - Sequence contains no matching element

  24: Console.WriteLine(list1.First(e => e.StudentId == 8));

  25:  

  26: StudentViewModel st1;

  27:  

  28: // returns the first element of the collection

  29: st1 = list1.FirstOrDefault();

  30:  

  31: // works

  32: st1 = list1.FirstOrDefault(e => e.StudentId == 1);

  33:  

  34: // returns the first element of the collection matches the query

  35: st1 = list1.FirstOrDefault(e => e.StudentId == 7);

  36:  

  37: // works st1 is null

  38: st1 = list1.FirstOrDefault(e => e.StudentId == 8);

 

The code snippets and the comments describes the functions of the Single and First extension methods and their helping counter parts xxOrDefault.

 About the semantic distinction.

If you know that your query should return only one element it’s better to use Single or SingleOrDefault. For example when you query a customer collection by customer ID you know that according to the business there’s only one customer ID with a specific value. First or FirstOrDefault could be used in the scenario but it doesn’t give you the semantics of the business logic.

And Single or SingleOrDefault throws an InvalidOperationException with the message ‘sequence contains more than one element’ if the collection has more than one element matching the query. This behavior sometimes can be used to validate.

Another common misuse of the above extension methods is chaining them unnecessarily with ‘where’ statement like this

   1: // this is a common misuse

   2: list1.Where(e => e.StudentId == 7).Single();

   3:  

   4: list1.Single(e => e.StudentId == 7);

 

If you want the code for the demonstration purpose you can download it here

SharePoint 2013 Item level Auditing

This post provides information about the item level auditing and how the server side object model works.

When you create site collection the auditing is not enabled by default. Each content database has a AuditData table to store the auditing information. You can enable the auditing at the site collection level through Site Settings => Site Collection Audit Settings. You can view the Audit Logs through Site Settings –> Audit Log Reports.

When no auditing is enabled and if you try to see / generate any of the available audit reports you will end up with a screen which says “Something went wrong”.

Now let’s walk through 2 scenarios to get some understanding about the SharePoint 2013 Item level Auditing.

Scenario 1 – No Auditing

image

Now in a clean new environment we will check the Audit status of the SPSite, SPWeb, SPList, SPListItem.

In the example we have a site collection and it has a Document Library names “AuditDocLib” and it has one document.

   1: private static void PerformAuditFeatureCheck()

   2: {

   3:     string url = @"http://singlespserver/";

   4:  

   5:     using(SPSite site = new SPSite(url))

   6:     {

   7:         var siteAuditFlags = site.Audit.AuditFlags;

   8:     

   9:         var topLevel = site.OpenWeb();

  10:         var topLevelAuditFlags = topLevel.Audit.AuditFlags;

  11:         

  12:         var sharedDocLib = topLevel.Lists.TryGetList("AuditDocLib");

  13:         var shareDocLibAuditFlags = sharedDocLib.Audit.AuditFlags;

  14:  

  15:         var doc1 = sharedDocLib.Items.GetItemById(1);

  16:         var doc1AuditFlags = doc1.Audit.AuditFlags;        

  17:  

  18:        }

  19: }

When we run the above code AuditFlags of the SPSite, SPWeb, SPList and SPListItem is None.

 

Scenario 2 – Enabling Audit for specific ListItem

image

Now we’ll enable the auditing for a specific document in the AuditDocLib.

   1: private static void EnableAuditForItem()

   2: {

   3:     string url = @"http://singlespserver/";

   4:  

   5:     using(SPSite site = new SPSite(url))

   6:     {

   7:         var topLevel = site.OpenWeb();

   8:         

   9:         var sharedDocLib = topLevel.Lists.TryGetList("AuditDocLib");

  10:         

  11:         // AuditFlag is None

  12:         var shareDocLibAuditFlags = sharedDocLib.Audit.AuditFlags;

  13:  

  14:         var doc1 = sharedDocLib.Items.GetItemById(1);

  15:         

  16:         // AuditFlag is None

  17:         var doc1AuditFlags = doc1.Audit.AuditFlags;        

  18:  

  19:         //enabling auditing for the item

  20:         doc1.Audit.AuditFlags = SPAuditMaskType.View;

  21:         doc1.Audit.Update();

  22:     }

  23: }

Now doc1 is audit enabled. And in the Audit Log Reports we can get the Audits. Below image shows the screen shot of the audit report after viewing the doc1 for few times. image

Notice that the report also has the log entries for the Library (AuditDocLib). It is obvious that if a person want to view an item in the library more often he/she has to navigate to the corresponding List/Library and view the Item. But we didn’t specify to audit the Library; in the code the AuditMask is set only for the item.

Again we’ll run the PerformAuditFeatureCheck method to check the AuditFlags

 image

You can see that the AuditFlags for the AuditDocLib is still None. This is because AuditFlags were not set for the AuditDocLib explicitly beacuse there’s no <NewAuditMask>4</NewAuditMask> event against the AuditDocLib.

This also hints us a point that just by retrieving the AuditFlags of a List / Library through the object model doesn’t give you the full insight of whether the particular List / Library is being audited. AuditFlags property gives the value of the last explicitly set AuditMask.

Accessing Contacts Information in Windows 8.1

Windows 8.1 SDK has lots of updates and changes compared to Windows 8 SDK. As an epitome of all these changes most of the Windows 8 SDK methods and classes have been deprecated and not advised to be used in the new development.

This post shows how to access the Contacts information in Windows 8.1 and 8. The new SDK is easy to use for sure and more sleek.

 Accessing Contacts Information using Windows 8 SDK

   1: private async void GetContactsDataUsingWindowd8SDK()

   2: {

   3:     var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();

   4:     contactPicker.CommitButtonText = "Select a contact";

   5:  

   6:     ContactInformation contact = await contactPicker.PickSingleContactAsync();

   7:  

   8:     // For multiple contacts Selection

   9:     //IReadOnlyList<ContactInformation> contacts = await contactPicker.PickMultipleContactsAsync();

  10:  

  11:     var viewmodel = new ContactViewModel();

  12:  

  13:     if (contact != null)

  14:     {

  15:         string name = contact.Name;

  16:  

  17:         if (contact.Emails.Count > 0)

  18:         {

  19:             StringBuilder sb = new StringBuilder();

  20:             foreach (IContactField field in contact.Emails)

  21:             {

  22:                 sb.AppendFormat("{0} ({1}) \n", field.Value, field.Category);

  23:             }

  24:             string email = sb.ToString();

  25:         }

  26:  

  27:         // Display the contact’s thumbnail

  28:         Windows.Storage.Streams.IRandomAccessStreamWithContentType stream = await contact.GetThumbnailAsync();

  29:  

  30:         if (stream != null && stream.Size > 0)

  31:         {

  32:             BitmapImage image = new BitmapImage();

  33:             image.SetSource(stream);

  34:         }

  35:     } 

 

Same to be done in Windows 8.1 SDK

   1: private async void GetContactInfoUsingWindows81SDK()

   2: {

   3:     var contactPicker = new Windows.ApplicationModel.Contacts.ContactPicker();

   4:     contactPicker.CommitButtonText = "Select Contacts";

   5:  

   6:     // select specific properties only

   7:     contactPicker.SelectionMode = Windows.ApplicationModel.Contacts.ContactSelectionMode.Fields;

   8:  

   9:     //contactPicker.DesiredFieldsWithContactFieldType.Add();

  10:  

  11:     var contactCollection = await contactPicker.PickContactsAsync();

  12:     //contactCollection.Add(new Windows.ApplicationModel.Contacts.Contact());

  13:  

  14:     List<ContactViewModel> contactViewModels = new List<ContactViewModel>();

  15:  

  16:     if (contactCollection != null)

  17:     {

  18:         foreach (var contact in contactCollection)

  19:         {

  20:             var viewmodel = new ContactViewModel();

  21:  

  22:             viewmodel.Id = contact.Id;

  23:             viewmodel.FullName = contact.FirstName + " " + contact.LastName;

  24:  

  25:             if (contact.Emails.Any())

  26:             {

  27:                 foreach (var email in contact.Emails)

  28:                 {

  29:                     if (email.Kind == Windows.ApplicationModel.Contacts.ContactEmailKind.Personal)

  30:                     {

  31:                         viewmodel.PersonalEmail = email.Address;

  32:                     }

  33:                 }

  34:             }

  35:  

  36:             var thumbnail = await contact.Thumbnail.OpenReadAsync();

  37:  

  38:             if (thumbnail != null && thumbnail.Size > 0)

  39:             {

  40:                 viewmodel.ContactThumbnail.SetSource(thumbnail);

  41:             }

  42:  

  43:  

  44:         }

  45:     }

  46: }

 

Code for the View Model class used in 8.1

   1: public class ContactViewModel

   2: {

   3:     public string Id { get; set; }

   4:  

   5:     public string FullName { get; set; }

   6:  

   7:     public string PersonalEmail { get; set; }

   8:  

   9:     public BitmapImage ContactThumbnail { get; set; }

  10: }

You can notice that 8.1 version the methods have changes and enums have been introduced to make the development more concise.

WinRT or WinMD Components

The WinRT platform provides developers with a new way to build reusable software components. WinRT types packaged in a WinRT component, also called a WinMD component (WinRT components == WinMD components). In traditional .NET application development, you could create a dynamic-link library (DLL) as a managed class library and use it in one or more applications.

This option is still available in Visual Studio. However, you can change the output type of a library project from class library to WinRT component, and the output of such a project is a WinMD (Windows Meta Data) file.

 

Benefits of creating a WinMD / WinRT component.

Contrasting to the DLL files WinRT / WinMD components can be accessed by unmanaged languages like C++ and Javascript. This allows us to create a WinRT / WinMD component in a managed language (C# or Visual Basic) and use it across different Windows Store Apps (WSA) projects implemented in unmanaged languages.

But this flexibility comes with some restrictions.

  • All public classes in WinRT / WinMD component should be marked sealed.
  • public fields aren’t allowed.
  • public data members must be declared as properties
  • Data types should be WinRT / WinMD compatible
  • Creating a WinRT / WinMD component in C++ has some additional property attribute settings (activatable class and ect…)

 

Creating a WinRT / WinMD component in C#

This is a very simple example and straight forward create Windows Store Apps (WSA) class library. Mark the class public and sealed. Create a method to return a string value.

image

Go to Project Properties and change the output type from Class Library to Windows Runtime Component. 

image

Component Code image

 

 

 

Do the build.

Accessing the C# WinRT in Javascript

Create a WSA project in Javascript. Add the WinRT project reference to the Javascript project.

Create a Javascript method to call the WinRT method. VS intellisense would guide you.

Javascript Code.

image

Here you can notice that the GetDate() C# method is formatted to getDate() with Javascript naming convention.

HTML Code

image

Run the code. 🙂

Things to consider

When you create a WinRT / WinMD component it is specifically designed for the Windows Runtime. Windows Runtime supports different language executions. In the above Javascript project we have a WinRT component developed in C# executed in the Javascript app. This is very useful when you want to extend your existing LOBs. You can use your existing managed code with little modifications, this cuts down development cost.

And also some complex data processing codes are hard to be written in pure Javascript. You can implement those in C++ and use the component in your front facing Javascript WSA.

Facebook like link sharing

When you paste a URL in the FB share, FB pulls out some information about the URL along with any available images.

I created a similar feature in ASP.NET few years ago and I got the project file while clearing out  one of my disk partitions and thought of sharing it.

This project uses HTMLAgilitPack (http://htmlagilitypack.codeplex.com/) and XPATH for processing the HTML.

A working preview.

image

 

 

 

 

 

 

You can download the code here (http://sdrv.ms/MM5zA1) from SkyDrive.

CRUD operations in Infragistics WebDataGrid

DataGrids are vital controls in many user interfaces. Like it or not still 2D tables are one of the most comfortable user interface elements in not only viewing data but also in performing rapid inserts and updates.

CRUD operations of DataGrid is not a big innovation, but there are plenty of ways to do that. This post explains how to perform CRUD operations using Infragistics WebDataGrid control in ASP.NET.

This code is written using Infragistics version 12.2

Create a simple WebDataGrid.

   1: <ig:WebDataGrid ID="WebGridCarton" runat="server" Height="350px" Width="100%" AutoGenerateColumns="False" 

   2:    DataKeyFields="CartonViewModelGuid" EnableDataViewState="True" 

   3:    OnRowAdded="WebGridCarton_RowAdded" OnRowAdding="WebGridCarton_RowAdding" 

   4:    OnRowDeleted="WebGridCarton_RowDeleted" OnRowsDeleting="WebGridCarton_RowsDeleting" 

   5:    OnRowUpdated="WebGridCarton_RowUpdated" OnRowUpdating="WebGridCarton_RowUpdating">

   6:    <Columns>

   7:        <ig:BoundDataField DataFieldName="CartonViewModelGuid" Key="CartonViewModelGuid" Hidden="true" HtmlEncode="true">

   8:        <Header Text="Guid">

   9:        </Header>

  10:        </ig:BoundDataField>

  11:        <ig:BoundDataField DataFieldName="CartonInfoId" Key="CartonInfoId" Hidden="true" HtmlEncode="true">

  12:            <Header Text="Id">

  13:            </Header>

  14:        </ig:BoundDataField>

  15:        <ig:BoundDataField DataFieldName="SelectionColumn" Key="SelectionColumn" HtmlEncode="true" Width="15">

  16:        </ig:BoundDataField>

  17:        <ig:BoundDataField DataFieldName="Size" Key="Size" HtmlEncode="true">

  18:            <Header Text="Size">

  19:            </Header>

  20:        </ig:BoundDataField>

  21:        <ig:BoundDataField DataFieldName="Length" Key="Length" HtmlEncode="true">

  22:            <Header Text="Length (cm)">

  23:            </Header>

  24:        </ig:BoundDataField>

  25:        <ig:BoundDataField DataFieldName="Width" Key="Width" HtmlEncode="true">

  26:            <Header Text="Width (cm)">

  27:            </Header>

  28:        </ig:BoundDataField>

  29:        <ig:BoundDataField DataFieldName="Height" Key="Height" HtmlEncode="true">

  30:            <Header Text="Height (cm)">

  31:            </Header>

  32:        </ig:BoundDataField>

  33:        <ig:BoundDataField DataFieldName="Volume" Key="Volume" HtmlEncode="true">

  34:            <Header Text="Volume (cbm)">

  35:            </Header>

  36:        </ig:BoundDataField>

  37:        <ig:BoundDataField DataFieldName="Weight" Key="Weight" HtmlEncode="true">

  38:            <Header Text="Weight (kg)">

  39:            </Header>

  40:        </ig:BoundDataField>

  41:        <ig:BoundDataField DataFieldName="WeightLimit" Key="WeightLimit" HtmlEncode="true">

  42:            <Header Text="Weight Limit (kg)">

  43:            </Header>

  44:        </ig:BoundDataField>

  45:    </Columns>

  46:    <Behaviors>

  47:        <ig:Activation Enabled="true"></ig:Activation>

  48:        <ig:Selection CellClickAction="Row" RowSelectType="Single" Enabled="true"></ig:Selection>

  49:        <ig:EditingCore AutoCRUD="false">

  50:            <Behaviors>

  51:                <ig:CellEditing EditModeActions-EnableF2="true" EditModeActions-MouseClick="Single" Enabled="true">

  52:                    <ColumnSettings>

  53:                        <ig:EditingColumnSetting ColumnKey="CartonInfoId" ReadOnly="true" />

  54:                        <ig:EditingColumnSetting ColumnKey="SelectionColumn" ReadOnly="true" />

  55:                        <ig:EditingColumnSetting ColumnKey="CartonViewModelGuid" ReadOnly="true" />

  56:                        <ig:EditingColumnSetting ColumnKey="Size" EditorID="DropDownProvider_Size" ValidatorID="RequiredFieldValidator1" />

  57:                        <ig:EditingColumnSetting ColumnKey="Length" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  58:                        <ig:EditingColumnSetting ColumnKey="Width" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  59:                        <ig:EditingColumnSetting ColumnKey="Height" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  60:                        <ig:EditingColumnSetting ColumnKey="Volume" EditorID="WebTextEditor1" ReadOnly="true" ValidatorID="DecimalValidator" />

  61:                        <ig:EditingColumnSetting ColumnKey="Weight" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  62:                        <ig:EditingColumnSetting ColumnKey="WeightLimit" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  63:                    </ColumnSettings>

  64:                    <EditModeActions MouseClick="Single" />

  65:                </ig:CellEditing>

  66:                <ig:RowAdding Alignment="Top" EditModeActions-EnableF2="true" EditModeActions-EnableOnActive="true"

  67:                    EditModeActions-MouseClick="Single" Enabled="true">

  68:                    <EditModeActions EnableOnActive="True" MouseClick="Single" />

  69:                    <ColumnSettings>

  70:                        <ig:RowAddingColumnSetting ColumnKey="SelectionColumn" ReadOnly="true" />

  71:                        <ig:RowAddingColumnSetting ColumnKey="Size" EditorID="DropDownProvider_Size" ValidatorID="RequiredFieldValidator1" />

  72:                        <ig:RowAddingColumnSetting ColumnKey="Length" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  73:                        <ig:RowAddingColumnSetting ColumnKey="Width" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  74:                        <ig:RowAddingColumnSetting ColumnKey="Height" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  75:                        <ig:RowAddingColumnSetting ColumnKey="Volume" EditorID="WebTextEditor1" ReadOnly="true" ValidatorID="DecimalValidator" />

  76:                        <ig:RowAddingColumnSetting ColumnKey="Weight" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  77:                        <ig:RowAddingColumnSetting ColumnKey="WeightLimit" EditorID="WebTextEditor1" ValidatorID="DecimalValidator" />

  78:                    </ColumnSettings>

  79:                </ig:RowAdding>

  80:                <ig:RowDeleting Enabled="true" />

  81:            </Behaviors>

  82:        </ig:EditingCore>

  83:    </Behaviors>

  84:    <EditorProviders>

  85:        <ig:TextBoxProvider ID="WebTextEditor1">

  86:            <EditorControl ID="EditorControl1" ClientIDMode="Static" runat="server"></EditorControl>

  87:        </ig:TextBoxProvider>

  88:        <ig:DropDownProvider ID="DropDownProvider_Size">

  89:            <EditorControl ClientIDMode="Predictable" DropDownContainerMaxHeight="100px" EnableAnimations="False"

  90:                EnableDropDownAsChild="False" ID="CartonSizeDrdEditor" DropDownContainerWidth="185px" AutoPostBack="true" 

  91:                EnableAjaxViewState="true">

  92:            </EditorControl>

  93:        </ig:DropDownProvider>

  94:    </EditorProviders>

  95: </ig:WebDataGrid>

This grid has one drop down editor and others fields are text box editors. And it supports CRUD. The grid control has 6 CRUD events associated with it. They’re RowAdding, RowAdded, RowUpdating, RowUpdated, RowDeleting and RowDeleted.

This is the ViewModel class used for data binding.

   1: public class CartonInfoGridViewModel

   2: {

   3:     public string CartonViewModelGuid { get; private set; }

   4:  

   5:     public int CartonInfoId { get; set; }

   6:     public string Size { get; set; }

   7:     public string Length { get; set; }

   8:     public string Width { get; set; }

   9:     public string Height { get; set; }

  10:     public string Volume { get; set; }

  11:     public string Weight { get; set; }

  12:     public string WeightLimit { get; set; }

  13:  

  14:     public CartonInfoGridViewModel()

  15:     {

  16:         this.CartonViewModelGuid = Guid.NewGuid().ToString();

  17:     }

  18: }

CartonViewModelGuid property is used identify the objects in the Session collection.

 

Adding Rows

In the RowAdding event we create a new ViewModel object with the values entered in the grid. And add it to the Session collection then we bind the Session collection to the grid. Please note these property names are same as the ones in the ASPX file.

   1: protected void WebGridCarton_RowAdding(object sender, Infragistics.Web.UI.GridControls.RowAddingEventArgs e)

   2: {

   3:     var cartonRow = new CartonInfoGridViewModel();

   4:  

   5:     cartonRow.CartonInfoId = -1;

   6:     cartonRow.Size = e.Values["Size"].ToString();

   7:     cartonRow.Length = e.Values["Length"].ToString();

   8:     cartonRow.Width = e.Values["Width"].ToString();

   9:     cartonRow.Height = e.Values["Height"].ToString();

  10:     cartonRow.Volume = e.Values["Volume"].ToString();

  11:     cartonRow.Weight = e.Values["Weight"].ToString();

  12:     cartonRow.WeightLimit = e.Values["WeightLimit"].ToString();

  13:  

  14:     if (this.Session["GridGridCartonsInfo"] != null && this.Session["GridCartonsInfo"] is List<CartonInfoGridViewModel>)

  15:     {

  16:         var collection = Session["GridCartonsInfo"] as List<CartonInfoGridViewModel>;

  17:         collection.Add(cartonRow);

  18:     }

  19:     else

  20:     {

  21:         var collection = new List<CartonInfoGridViewModel>();

  22:         collection.Add(cartonRow);

  23:     }

  24:  

  25:     this.DataBindCaronInfoGrid();

  26: }

DataBindCaronInfoGrid method binds the Session data to the Grid.

Just to prevent any Javascript errors popping up during this process in the RowAdded event we suppress the exceptions.

   1: protected void WebGridCarton_RowAdded(object sender, Infragistics.Web.UI.GridControls.RowAddedEventArgs e)

   2: {

   3:     e.ExceptionHandled = true;

   4: }

 

Deleting Rows

To delete a row we get the Id of the object and remove it from the Session collection. And bind the Session data to the grid.

   1: protected void WebGridCarton_RowsDeleting(object sender, Infragistics.Web.UI.GridControls.RowDeletingEventArgs e)

   2: {

   3:     string id = e.Row.DataKey.First().ToString();

   4:  

   5:     if (this.Session["GridGridCartonsInfo"] != null && this.Session["GridCartonsInfo"] is List<CartonInfoGridViewModel>)

   6:     {

   7:         var collecion = this.Session["GridGridCartonsInfo"] as List<CartonInfoGridViewModel>;

   8:  

   9:         var removeItem = collecion.FirstOrDefault(c => c.CartonViewModelGuid == id);

  10:  

  11:         if (removeItem != null)

  12:             collecion.Remove(removeItem);

  13:     }

  14:  

  15:     this.DataBindCaronInfoGrid();

  16: }

  17:  

  18: protected void WebGridCarton_RowDeleted(object sender, Infragistics.Web.UI.GridControls.RowDeletedEventArgs e)

  19: {

  20:     e.ExceptionHandled = true;

  21: }

 

Updating Rows

Updating the rows happens the same way, we identify the object via the Id and update the values.

   1: protected void WebGridCarton_RowUpdating(object sender, Infragistics.Web.UI.GridControls.RowUpdatingEventArgs e)

   2: {

   3:     if (Session["GridGridCartonsInfo"] != null && Session["GridGridCartonsInfo"] is List<CartonInfoGridViewModel>)

   4:     {

   5:         var collection = Session["GridGridCartonsInfo"] as List<CartonInfoGridViewModel>;

   6:  

   7:         if (e.OldValues["CartonViewModelGuid"].ToString() == e.Values["CartonViewModelGuid"].ToString())

   8:         {

   9:             string id = e.Values["CartonViewModelGuid"].ToString();

  10:  

  11:             var editItem = collection.FirstOrDefault(c => c.CartonViewModelGuid == id);

  12:  

  13:             if (editItem != null)

  14:             {

  15:                 editItem.CartonInfoId = Convert.ToInt32(e.Values["CartonInfoId"]);

  16:                 editItem.Size = e.Values["Size"].ToString();

  17:                 editItem.Length = e.Values["Length"].ToString();

  18:                 editItem.Width = e.Values["Width"].ToString();

  19:                 editItem.Height = e.Values["Height"].ToString();

  20:                 editItem.Volume = e.Values["Volume"].ToString();

  21:                 editItem.Weight = e.Values["Weight"].ToString();

  22:                 editItem.WeightLimit = e.Values["WeightLimit"].ToString();

  23:             }

  24:         }

  25:  

  26:         this.DataBindCaronInfoGrid();

  27:     }          

  28: }

  29:  

  30: protected void WebGridCarton_RowUpdated(object sender, Infragistics.Web.UI.GridControls.RowUpdatedEventArgs e)

  31: {

  32:     e.ExceptionHandled = true;

  33: }

 

Note

In the ViewModel class CartonViewModelGuid property is used as the object identifier in the collection. (In this case collection is persisted in the ASP.NET Session) For the new rows we set the CartonInfoId to –1, because this is an identity column in the database.

When we update the objects which are stored in the database already we can’t change the CartonInfoId that’s the reason why we use a separate Id property for the ViewModels.

 

The code for the DataBindCaronInfoGrid method

   1: private void DataBindCaronInfoGrid()

   2: {

   3:     if (this.Session["GridCartonsInfo"] != null && this.Session["GridCartonsInfo"] is List<CartonInfoGridViewModel>)

   4:     {

   5:         this.WebGridCarton.DataSource = Session["GridCartonsInfo"] as List<CartonInfoGridViewModel>;

   6:         this.WebGridCarton.DataBind();

   7:     }

   8:     else

   9:         this.LblStatus.Text = "Session expired, Please reload the page.";

  10: }