Share your knowledge and create a knowledgebase.

Archive for the ‘.Net’ Category



This article is based on a single table schema (don’t worry subsequent parts will see a much more complex database schema!) so that you can get to grips with LINQ to SQL (the name for using LINQ with relational data – also known as DLINQ in a previous life, but we won’t go into that…) quickly and feel confident with this awesome new language enhancement.

Conceptual data access layer?

Conceptual what?! If you are familiar with the concept of a database schema then you don’t have much to worry about! You can think of a conceptual data access layer as being your applications view of the database.

I don’t want to bog you down with entities yet! That will come in part 3 of this series. Just know that what we deal with from our applications point of view is a series of entities describing the tables and relationships in our database schema. I will let you look forward to the diagrams explaining all of this in the next part!

Generating our DAL using Visual Studio Orcas

Typically DAL’s have consisted of boilerplate code to map relational data to objects and vice versa, speaking from experience I really enjoyed this the first time I did it – but now it just annoys the heck out of me! With that in mind it’s no surprise that the folks on the Visual Studio team decided to give us a few tools to generate that boilerplate code.

At present there are two tools that come with Visual Studio Orcas (both were in the LINQ May 2006 CTP):

  • SQLMetal.exe – a command line tool that gives you a tonne of options when creating your DAL. Things of note are pluralisation of entities, extracting stored procedures from your database schema, and defining the namespace, as well as a tonne of other stuff.
  • LINQ to SQL file – used to be called DLINQ objects in the LINQ May 2006 CTP, now it has a cooler name, but it’s pretty much the same tool. This is a file template that lives in Visual Studio Orcas – it’s a designer. Personally I don’t really like stuff that is “drag and drop” so I tend to opt for the finer control offered to me by SQLMetal – with that said it’s a bit of a shock to me that I have opted to use the designer for this part of the series.

Before we go any further I just want to give you a quick snapshot of the table that we will be using for this part of the series – like I mentioned earlier the next part will have several tables with many relationships…think of it as a throwaway schema (actually I don’t think if even qualifies as a database schema it’s so simple!).

Figure 2-1: Our unbelievably simple database schema (for now!)

Our unbelievably simple database schema (for now!)

I bet you are questioning my credibility now aren’t you?! Come on its simple so that you can learn the basics quick so when we look at more complex examples in the future you have a good solid understanding of using LINQ with relational data.

Ok, go ahead and add some dummy data to that table.

Fire up Visual Studio Orcas (the March 2007 CTP remember) and create a new .NET 3.5 Console Application. When you’ve done that right click the project and add a LINQ to SQL File – as all we’ll be throwing away all of the code in this part just name it Part2.dbml for now.

Figure 2-2: Adding a LINQ to SQL File to our solution

Adding a LINQ to SQL File to our solution

At this moment in time we have everything setup so drag the Books table from the server explorer window onto the canvas of Part2.dbml.

Figure 2-3: Result of dragging the Books table onto Part2.dbml

Result of dragging the Books table onto Part2.dbml

If you take a look at the code that has been generated for us you will notice there is a class called Book that implements a few interfaces (more on those in the next part). Take a look at the attribute that is placed before the class definition (fig. 2-4) here we are associating this type (Book) with Books table in our database.

Figure 2-4: Associating the Book type with our Books table

//…
[System.Data.Linq.Table(Name="dbo.Books")]
//…

Take a moment to look at the fields in the Book type – notice that the designer has created two fields of the same names as the columns in the Books table; properties have also been defined in the class marked with an attribute to associate the fields with their corresponding column in the Books table (Fig. 2-5).

Figure 2-5: Associating the fields in the Book type with the columns in our Books table

//…
[System.Data.Linq.Column(Storage="_BookId",
  Name="BookId",
  DBType="Int NOT NULL IDENTITY",
  IsPrimaryKey=true,
  IsDBGenerated=true,
  CanBeNull=false)]
//…
[System.Data.Linq.Column(Storage="_Title",
  Name="Title",
  DBType="NVarChar(50) NOT NULL",
  CanBeNull=false)]
//…

Before we move on we will talk a little about the Column attribute (defined in the System.Data.Linq namespace). Take a look at Fig. 2-5, notice that we explicity state the field that stores the value of the column, e.g. the BookId columns’ value is stored in the _BookId field, we also state that this property is associated with the BookId column in the Books table (Name="BookId").

If you are familiar with SQL Server data types then you will also know that there is not a 1..1 mapping between SQL Server 2005 data types and CLR types, e.g. the CLR has no money data type. For this reason we can explicitly state the database column type, the designer will choose the most appropriate type for use for that particular property, e.g. for the BookId column the designer has chosen to use the default int value type (Int32).

Before we move on, just be aware that you have to explicitly state what values are generated by the database, and whether or not they can be null. In the case of BookId the database generates a new integer automatically for each row; this column is also a primary key.

Take some time to further examine the Column and Table attributes.

Querying our DAL

We will now go through a few simple queries against the DAL that we have created in previous steps.

For each example I have provided the query version and the lambda expression version.

Select all books

Figure 2-6: Selecting all books (query)

using System;
using System.Linq;

namespace Org.GBarnett.Dns.Linq {
  public class Program {
    public static void Main() {
      Part2DataContext db = new Part2DataContext();
      var query = from b in db.Books select b;
      foreach (var book in query) {
        Console.WriteLine(“{0} {1}”, book.BookId, book.Title);
      }
    }
  }
}

Figure 2-7: Selecting all books (lambda expressions/extension methods)

using System;
using System.Linq;

namespace Org.GBarnett.Dns.Linq {
  public class Program {
    public static void Main() {
      Part2DataContext db = new Part2DataContext();
      var query = db.Books.Select(x => x);
      foreach (var book in query) {
        Console.WriteLine(“{0} {1}”, book.BookId, book.Title);
      }
    }
  }
}

I should note that in fig. 2-7 you can omit the Select extension method and you will achieve the same result as when including the Select extension method and its lambda expression parameter; however, the example code given is clearer than when omitting the Select extension method.

Select the titles of the books whose title length is greater than 6 characters

Figure 2-8: Selecting the titles of the books with length > 6 (query)

using System;
using System.Collections.Generic;
using System.Linq;

namespace Org.GBarnett.Dns.Linq {
  public class Program {
    public static void Main() {
      Part2DataContext db = new Part2DataContext();
      IEnumerable<string> query =
        from b in db.Books where b.Title.Length > 6 select b.Title;
      foreach (string title in query) {
        Console.WriteLine(“{0}”, title);
      }
    }
  }
}

Figure 2-9: Selecting the titles of the books with length > 6 (lambda expressions/extension methods)

using System;
using System.Collections.Generic;
using System.Linq;
namespace Org.GBarnett.Dns.Linq {
  public class Program {
    public static void Main() {
      Part2DataContext db = new Part2DataContext();
      IEnumerable<string> query =
        db.Books.Where(x => x.Title.Length > 6).Select(x => x.Title);
      foreach (string title in query) {
        Console.WriteLine(“{0}”, title);
      }
    }
  }
}

Jul 30, 2008 Author: Ashish | Filed under: .Net

Language Integrated Query (LINQ, pronounced “link”) is a Microsoft .NET Framework component that adds native data querying capabilities to .NET languages using a syntax reminiscent of SQL. LINQ was released as a part of .NET Framework 3.5 on November 19, 2007.

LINQ defines a set of query operators that can be used to query, project and filter data in arrays, enumerable classes, XML, relational database, and third party data sources. While it allows any data source to be queried, it requires that the data be encapsulated as objects. So, if the data source does not natively store data as objects, the data must be mapped to the object domain. Queries written using the query operators are executed either by the LINQ query processing engine or, via an extension mechanism, handed over to LINQ providers which either implement a separate query processing engine or translate to a different format to be executed on a separate data store (such as on a database server as SQL queries). The results of a query are returned as a collection of in-memory objects that can be enumerated.

Standard Query Operators

The set of query operators defined by LINQ are exposed to the user as the Standard Query Operator API. The query operators supported by the API are:

Select / SelectMany Further information: Map (higher-order function)

The Select statement is used to perform a projection on the collection to select either all the data members that make up the object or a subset of it. The SelectMany operator is used to perform a one-to-many projection, i.e., if the objects in the collection contain another collection as a data member, SelectMany can be used to select the entire sub-collection. The user supplies a function, as a delegate, which selects the data members. The delegate is invoked on all the objects to project out the unneeded data members. Selection creates an object of a different type, which has either same or as many data members as the original class. The class must be already defined for the code to be compilable.

Where Further information: Filter (higher-order function)

The Where operator allows the definition of a set of predicate rules which are evaluated for each object in the collection, and objects which do not match the rule are filtered away. The predicate is supplied to the operator as a delegate.

Sum / Min / Max / Average / Aggregate Further information: Fold (higher-order function)

These operators take a predicate that retrieves a certain numeric value from each element in the collection and uses it to find the sum, minimum, maximum, average or aggregate values of all the elements in the collection, respectively.

Join / GroupJoin
The Join operator performs an inner join on two collections, based on matching keys for objects in each collection. It takes two functions as delegates, one for each collection, that it executes on each object in the collection to extract the key from the object. It also takes another delegate via which the user specifies which data elements, from the two matched elements, should be used to create the resultant object. The GroupJoin operator is used to perform a group join. Like the Select operator, the results of a join are instantiations of a different class, with all the data members of both the types of the source objects, or a subset of them.

Take / TakeWhile
The Take operator is used to select the first n objects from a collection, while the TakeWhile operator, which takes a predicate, selects those objects which match the predicate.

Skip / SkipWhile
The Skip and SkipWhile operators are complements of Take and TakeWhile - they skip the first n objects from a collection, or those objects which match a predicate (for the case of SkipWhile).

OfType
The OfType operator is used to select the elements of a certain type.

Concat
The Concat operator concatenates two collections.

OrderBy / ThenBy
The OrderBy operator is used to specify the primary sort ordering of the elements in a collection according to some key. The default ordering is in ascending order, to reverse the order the OrderByDescending operator is to be used. ThenBy and ThenByDescending specifies subsequent ordering of the elements. The function to extract the key value from the object is specified by the user as a delegate.

Reverse
The Reverse operator reverses a collection.

GroupBy
The GroupBy operator takes a delegate that extracts a key value and returns a collection of IGrouping<Key, Values> objects, for each distinct key value. The IGrouping objects can then be used to enumerate all the objects for a particular key value.

Distinct
The Distinct operator removes duplicate instances of a key value from a collection. The function to retrieve the key value is to be supplied as a delegate.

Union / Intersect / Except
These operators are used to perform a union, intersection and difference operation on two sequences, respectively.

EqualAll
The EqualAll operator checks if all elements in two collections are equal.

First / FirstOrDefault / Last / LastOrDefault

These operators take a predicate. The First operator returns the first element for which the predicate yields true or throws an exception if nothing matches. The FirstOrDefault operator is like the First operator except that it returns the default value for the element type (usually a null reference) in case nothing matches the predicate. The last operator retrieves the last element to match the predicate, or throws an exception in case nothing matches. The LastOrDefault returns the default element value if nothing matches.

Single
The Single operator takes a predicate and returns the element which matches the predicate. An exception is thrown if none or more than one elements match the predicate.

ElementAt
The ElementAt operator retrieves the element at a given index in the collection.

Any / All / Contains
The Any operator checks if there are any element in the collection matching the predicate. It does not select the element, but returns true for a match. The All operator checks if all elements match the predicate. The Contains operator checks if the collection contains a given value.

Count
The Count operator counts the number of elements in the given collection.

The Standard Query Operator also specifies certain operators which converts a collection into other types:

* AsEnumerable: converts the collection to IEnumerable<T> type.
* ToQueryable: converts the collection to IQueryable<T> type.
* ToArray: converts the collection to an array.
* ToList: converts the collection to IList<T> type.
* ToDictionary: converts the collection to IDictionary<T, K> type, indexed by the key K.
* ToLookup: converts the collection to ILookup<T, K> type, indexed by the key K.
* Cast: converts a non-generic IEnumerable collection to one of IEnumerable<T> by casting each element to type T. Throws an exception for incompatible types.
* OfType: converts a non-generic IEnumerable collection to one of IEnumerable<T>. Only elements of type T are included.

The query operators are defined in the IEnumerable<T> interface as generic extension methods, and a concrete implementation is provided in the Sequence class. As a result, any class which implements the IEnumerable<T> interface has access to these methods and are queryable. LINQ also defines a set of generic Func delegates, which define the type of delegates handled by the LINQ query methods. Any function wrapped in a Func delegate can be used by LINQ. Each of these methods return an IEnumerable<T>, so the output of one can be used as input to another, resulting in query composability. The functions, however, are lazily evaluated, i.e., the collections are enumerated only when the result is retrieved. The enumeration is halted as soon as a match is found, and the delegates evaluated on it. When a subsequent object in the resultant collection is retrieved, the enumeration of the source collection is continued beyond the element already evaluated. However, grouping operations, like GroupBy and OrderBy, as well as Sum, Min, Max, Average and Aggregate, require data from all elements in collection, and force an eager evaluation. LINQ does not feature a query optimizer and the query operators are evaluated in the order they are invoked. The LINQ methods are compilable in .NET Framework 2.0, as well.

Language Extensions

While LINQ is primarily implemented as a library for .NET Framework 2.0, it also defines a set of language extensions that can be optionally implemented by languages to make queries a first class language construct and provide syntactic sugar for writing queries. These language extensions have initially been implemented in C# 3.0, VB 9.0 and Chrome, with other languages like F# and Nemerle having announced preliminary support. The language extensions include:

* Query syntax: Languages are free to choose a query syntax, which it will recognize natively. These language keywords must be translated by the compiler to appropriate LINQ method calls. The languages can implement operator reordering and other optimizations at the keyword level.
* Implicitly typed variables: This enhancement allows variables to be declared without specifying their types. The languages C# 3.0 and Chrome declare them with the var keyword. In VB9.0, the use of the Dim keyword without type declaration accomplishes the same declaration. Such objects are still strongly typed; for these objects the compiler uses type inference to infer the type of the variables. This allows the result of the queries to be specified and their result defined without declaring the type of the intermediate variables.
* Anonymous types: Anonymous types allow classes, which contain only data member declarations, to be inferred by the compiler. This is useful for the Select and Join operators, whose result types may differ from the types of the original objects. The compiler uses type inference to determine the fields contained in the classes and generates accessors and mutators for these fields.
* Object Initializer: Object initializers allow an object to be created and initialized in a single scope, this allows creation of delegates that extract fields from an object, create a new object and assign the extracted data to the fields of the new object in a single statement, as is required for Select and Join operators.
* Lambda expressions: Lambda expressions are used to create delegates inline with other code. This allows the predicates and extraction functions to be written inline with the queries.

For example, in the query to select all the objects in a collection with SomeProperty less than 10,

int SomeValue = 5;

var results =  from c in SomeCollection
let x = SomeValue * 2
where c.SomeProperty < x
select new {c.SomeProperty, c.OtherProperty};

foreach (var result in results)
Console.WriteLine(result);

the types of variables result, c and results all are inferred by the compiler - assuming SomeCollection is IEnumerable<SomeClass>, c will be SomeClass, results will be IEnumerable<SomeOtherClass> and result will be SomeOtherClass, where SomeOtherClass will be a compiler generated class with only the SomeProperty and OtherProperty properties and their values set from the corresponding clauses of the source objects. The operators are then translated into method calls as:

IEnumerable<SomeOtherClass> results =
SomeCollection.Where
(
c => c.SomeProperty < (SomeValue * 2)
)
.Select
(
c => new {c.SomeProperty, c.OtherProperty}
)
foreach (SomeOtherClass result in results)
Console.WriteLine( result.ToString() );

LINQ Providers

LINQ also defines another interface, IQueryable<T>, which defines the same interfaces to the Standard Query Operators as IEnumerable<T>. However, the concrete implementation of the interface, instead of evaluating the query, converts the query expression, with all the operators and predicates, into an expression tree. The Expression tree preserves the high level structure of the query and can be examined at runtime. The type of the source collection defines which implementation will run - if the collection type implements IEnumerable<T>, it executes the local LINQ query execution engine and if it implements the IQueryable<T> implementation, it invokes the expression tree-based implementation. An extension method is also defined for IEnumerable<T> collections to be wrapped inside an IQueryable<T> collection, to force the latter implementation.

The expression trees are at the core of LINQ extensibility mechanism, by which LINQ can be adapted for any data source. The expression trees are handed over to LINQ Providers, which are data source-specific implementations that adapt the LINQ queries to be used with the data source. The LINQ Providers analyze the expression trees representing the query (”query trees”) and generate a DynamicMethod (which are methods generated at runtime) by using the reflection APIs to emit CIL code. These methods are executed when the query is run. LINQ comes with LINQ Providers for in-memory object collections, SQL Server databases, ADO.NET datasets and XML documents. These different providers define the different flavors of LINQ:

LINQ to Objects
The LINQ to Objects provider is used for querying in-memory collections, using the local query execution engine of LINQ. The code generated by this provider refer the implementations of the standard query operators as defined in the Sequence class and allows IEnumerable<T> collections to be queried locally.

LINQ to XML
The LINQ to XML provider converts an XML document to a collection of XElement objects, which are then queried against using the local execution engine that is provided as a part of the implementation of the standard query operator.

LINQ to SQL
The LINQ to SQL provider allows LINQ to be used to query SQL Server databases as well as SQL Server Compact databases. Since SQL Server data resides on a remote server, and because it already includes a querying engine, LINQ to SQL does not use the query engine of LINQ. Instead, it converts a LINQ query to SQL query which is then sent to SQL Server for processing. However, since SQL Server stores the data as relational data and LINQ works with data encapsulated in objects, the two representations must be mapped to one another. For this reason, LINQ to SQL also defines the mapping framework. The mapping is done by defining classes which corresponds to the tables in database, and containing all or a subset of the columns in the table as data members. The correspondence, along with other relational model attributes such as primary keys are specified using LINQ to SQL-defined attributes. For example,

[Table(Name="Customers")]
public class Customer
{
[Column(IsPrimaryKey = true)]
public int CustID;

[Column]
public string CustName;
}

this class definition maps to a table named Customers and the two data members correspond to two columns. The classes must be defined before LINQ to SQL can be used. Visual Studio 2008 includes a mapping designer which can be used to create the mapping between the data schemas in the object as well as relational domain. It can automatically create the corresponding classes from a database schema, as well as allow manual editing to create a different view by using only a subset of the tables or columns in a table.

The mapping is implemented by the DataContext which takes a connection string to the server, and can be used to generate a Table<T> where T is the type that the database table will be mapped to. The Table<T> encapsulates the data in the table, and implements the IQueryable<T> interface, so that the expression tree is created, which the LINQ to SQL provider handles. It converts the query into T-SQL and retrieves the result set from the database server. Since the processing happens at the database server, local methods, which are not defined as a part of the lambda expressions representing the predicates, cannot be used. However, it can use the stored procedures on the server. Any changes to the result set are tracked and can be submitted back to the database server.

LINQ to DataSets
The LINQ to SQL provider works only with Microsoft SQL Server databases; to support any generic database, LINQ also includes the LINQ to DataSets, which uses ADO.NET to handle the communication with the database. Once the data is in ADO.NET Datasets, LINQ to Datasets execute queries against these datasets.

Other providers
The LINQ providers can be implemented by third parties for various data sources as well. Several database server specific providers are available from the database vendors. Some of the popular providers include:

* Data Services: LINQ to ADO.NET Data Services
* Entity Framework: LINQ to Entities
* DbLinq: LINQ to MySQL, PostgreSQL, and Oracle
* Google search: LINQ to Google
* Windows Search: LINQ to System Search


Jul 30, 2008 Author: Ashish | Filed under: .Net

Developers often cringe when they have to create reports, but there are numerous options available with .NET to simplify the process. Tony Patton offers you a look at .NET’s various reporting options.
Data is the lifeblood of the enterprise and almost every application you develop will tap data in some manner. With all that data, users eventually want to view it in a report.

Many novice developers foolishly tackle reporting by creating custom forms—but this legwork is not necessary, as there are plenty of options with .NET for creating reports by dragging and dropping or a custom object model. In addition, reporting solutions offer additional functionality like PDF generation and charting that is hard to duplicate with custom code. .NET’s choices include readily available options, third-party products, and open source solutions. Let’s take a closer look at the reporting options available with .NET by category.

Readily available reporting options
I was very excited when I first encountered .NET with Visual Studio .NET. My enthusiasm stemmed from the inclusion of Crystal Reports within the environment. That’s right, no more purchasing and installing the product (although a standalone version is still available). My excitement was quickly extinguished as I tackled my first product utilizing Crystal Reports functionality. Its documentation is arcane and getting everything working properly is a bit mind-numbing. As time as passed, a few books have appeared on the subject and there are numerous Web resources as well. One in particular that I would recommend is Crystal Reports .NET Programming by Brian Bischof.

While Crystal Reports is readily available, the Microsoft Office Suite is seemingly everywhere as well. You may utilise Excel or Word functionality in a Windows client application or use the Office Web Components for browser clients. The different applications are readily accessible via your .NET code. Or, your enterprise may opt for another suite such as StarOffice or OpenOffice. Whatever the product, you can take advantage of it in your application. If these products are not a viable option, there are plenty of third-party products available.

Third-party products
Here is a sampling of third-party .NET reporting solutions. Free trial versions are available for most products, so you can give it a test run before making a commitment. Of course, there are open source options available as well.

  • ActiveReports for .NET: A managed implementation of the popular ActiveReports engine and report viewer. It provides complete code integration with the Visual Studio .NET environment. It supports both Web and Windows clients and allows exporting to PDF, Excel, RTF, HTML text, and TIFF images. The documentation is thorough and the drag-and-drop interface is straightforward.
  • ComponentOne Studio for .NET: It includes two tools: the report component, which generates Access-style database reports, and its companion Report Designer for report layout. It also includes tools for migrating Crystal Reports to their environment.
  • OOReport.NET: This product provides reports for Web-based clients. It also includes controls for assembling repots.
  • Visual Reports: This may be used with pre-.NET Visual Studio projects. It also includes an interactive report designer for proper layout. Report properties and such are accessed via COM interface.
  • Windward Reports: This product facilitates report design and creation via Microsoft Word. Open source reporting options

In the past, the term open source would never be uttered when discussing Microsoft-based development, but the .NET Framework’s acceptance and use of (some) standards has fueled several great open source solutions for .NET. And, reporting has a number of options.

Charting capabilities are provided by NPlot. One of the great features of .NET is the lack of adherence to one particular language, so you can use other products that are not .NET-based. While this may be beyond many .NET developers, open source solutions like the Java-based JasperReports can provide clean and simple reports.

Don’t overlook SQL Server
I would be remiss not to mention SQL Server’s Reporting Services. Microsoft describes it as “a comprehensive, server-based reporting solution designed to help you author, manage, and deliver both paper-based and interactive Web-based reports.” It is an excellent solution where SQL Server is readily available. It was first introduced as an add-on for SQL Server 2000, but it is also included with SQL Server 2005. It includes a report builder for simplifying report creation. SQL Server 2005 Reporting Services does not require Visual Studio .NET like its predecessor, but it may be utilised.

Make the data presentable
Where there is data there is a need to make sense of it, and reporting is one tool to aid users in this chore. Luckily, the .NET Framework includes various options ranging from the inherent Crystal Reports to open source alternatives. Your choices may depend on cost, but each product offers plenty of features to enhance your application.


May 14, 2008 Author: Ashish | Filed under: .Net

The .NET Framework provides enterprise services for building highly scalable solutions, but the implementation can be tricky. Learn when and where these services should be used.

One of the biggest mistakes architects can make when designing Enterprise Services is to assume that its sole purpose is to provide wrappers for existing unmanaged COM+ and MTS functionality. It’s a common assumption, given that the current Enterprise Services implementation provides little more than a managed interface to unmanaged COM+.

However, Microsoft views Enterprise Services quite differently. In its eyes, Enterprise Services is the replacement for COM+ and MTS. Going forward, Microsoft has two primary goals for new versions of the .NET Framework, starting with release 1.1 that’s due with Windows .NET Server 2003. First, all existing COM+ functionality will move to managed code. Second, all new distributed systems management functionality will be implemented in Enterprise Services in .NET. In fact, COM+ version 1.5 (released with Windows XP) will likely be the last version of COM+ available as an unmanaged release.

What is Enterprise Services?
Given Microsoft’s emphasis on Enterprise Services as its distributed systems development platform, you need to understand exactly what services Microsoft is talking about. Enterprise Services supports resource management in a distributed environment. This functionality includes support for distributed transactions, role-based security and packaging of objects, object pooling, just-in-time-activation (JITA), queued components, and loosely coupled events. When used together, these functions give architects the tools to implement a complete server application process model.

One of the most confusing aspects of Enterprise Services is when to use them. If your existing systems use COM+ extensively, you’ll naturally want to use Enterprise Services when porting your applications to .NET. But if you don’t use COM+ today, how do you get started and what’s the benefit?

Implementing transaction support
To understand how you can get started with Enterprise Services, let’s look at how you implement transaction support in three different scenarios: traditional distributed applications, Web services, and ASP.NET applications. It’s important to differentiate between how developers handle transactions in the COM+ world vs. .NET. In COM+, developers manually start a transaction and then check conditions to determine whether the transaction should be committed or aborted. But .NET lets you handle transaction commits and aborts automatically by using declarations exposed through attributes. In other words, you can think of distributed transactions as automatic or declarative transactions in .NET. (Developers still have the option of coding commits and aborts manually if they need that granular level of control.)

Traditional distributed applications
For a traditional distributed application, the developer needs to create a server object and then code the client to call the server object. Here’s a simple implementation of a server object implemented in C#:

View Code CSHARP
using System;
using System.EnterpriseServices;
[assembly : ApplicationName("TxDemo")]
 
[Transaction]
public class Account : ServicedComponent {
[AutoComplete]
public void Credit() {
// do some work
} }

We should note three important items from this sample. First, the statement, using System.EnterpriseServices; gives you access to attributes in the Enterprise Services namespace. Second, the Account class inherits from the existing ServicedComponent class, where it gets its ability to be managed by EnterpriseServices. Finally, the Transaction and AutoComplete attributes make objects instantiated from this class transactional. The AutoComplete attribute tells EnterpriseServices to commit the work done in the Credit function unless an error occurs, in which case it should abort. You still can catch exceptions and swallow them (i.e., not Throw them up the call stack) with AutoComplete committing the transaction.

Here’s the code required to use the Credit object from a client application:

View Code CSHARP
public Class AccountClient {
private void Transactions_Click() {
Account account = New Account();
order.Credit()
} }

Notice that the client has no idea that the server object uses transaction management.

Web Services
If your users need to consume resources managed by Enterprise Services but access them through a Web Services interface, you can expose those resources using parameters on the WebMethod attribute declaration. For example, the code below allows the WebMethod DeleteAuthor to create a new transaction before attempting to delete the author passed in by the user of the Web service:

View Code CSHARP
[WebMethod(TransactionOption=
TransactionOption.RequiresNew)]
public int DeleteAuthor(string lastName) {
//…
}

Any other business objects called by this WebMethod automatically inherit its transaction context as long as they are marked to either Support or Require a transaction.

ASP.NET applications
Using transactions from ASP Pages is simple, as well. You add a page-level directive like this:

View Code CSHARP
<%@ Page Transaction="Required" %>

The Required setting allows this page to initiate a new transaction if it’s not already participating in an existing one. Any components called by this page will participate in the same transaction context.

What’s the benefit of Enterprise Services?
Before we look at the benefit of Enterprise Services, let’s first consider the widely discussed downside: performance. It’s true that calling serviced components is slower than calling other objects. This is because creating context and crossing context boundaries imposes overhead on your applications. But calling serviced components hosted in library applications (these run in-process with the application) is nearly as fast as calling nonserviced components. Server applications have a higher call time due to crossing process or machine boundaries. You determine whether components run as library or server applications when you install them in COM+.

But here’s the real issue: In most real-world applications, the performance cost of the infrastructure required to support Enterprise Services is minimal compared to the cost of the actual work that the component does. Moreover, if you didn’t use the features provided by serviced components, your code will likely have to do additional work to get the same functionality.

So when should you use serviced components? In general, if your application architecture can benefit from the services (like guaranteed transactions) provided by Enterprise Services, serviced components are worth the performance cost. You should also consider the ease of development and future expansion benefits of developing with serviced components. Development is simpler with serviced components. If you use a SqlTransaction object or OleDbTransaction object to manage transactions, you’ll have to manage the transaction yourself. With Enterprise Services, you simply add a Transaction attribute to your object(s). Enterprise Services ensures that transactions managed between multiple objects happen logically and consistently. For example, you won’t have to add any special logic to determine whether an object is the root of a transaction.

Future expansion is easier to accomplish if you code systems using serviced components from the start. Suppose you create an order management system that includes inventory tables managed as part of the overall order transaction. What if, in the future, the inventory tables move to a separate database? What if the inventory object is moved to a remote server? What if one of the objects wants to dispatch to a transactional message queue? If you built the system with manual transaction management, you’ll have to rewrite it in order to make it work in a distributed environment. Had you built the system using Enterprise Services, you could make these deployment decisions without requiring changes to the underlying application.

It will take time to learn how to implement serviced components correctly. And you may not need to use them for many of your smaller applications. But employing their functionality correctly for distributed applications will pay off in the long term because they allow you to make deployment decisions without regard for design decisions you made during development.


May 14, 2008 Author: Ashish | Filed under: .Net

Perhaps the two best examples of this axiom are the data-bound control from Visual Basic 3.0 and the Visual Interdev Design Time Control (DTC). The VB3 data-bound control made great demos, but its performance effects on the underlying database had even Microsoft’s own consulting services group recommending against its use in a production application.

Interdev DTCs are legendary for the number of developers who were suckered into using them for a simple application, only to find that they had to rewrite the functionality from scratch when they wanted to extend them or modify their output because the DTC was neither modifiable nor extensible. Consequently, when I first saw the .NET DataSet object, I was cautiously optimistic. Unfortunately, many developers chose not to be cautious at all.What’s wrong with the DataSet?
I’m not saying that there’s anything inherently wrong with the DataSet object. But it’s like any other tool—you need to understand how to use it appropriately. Although it’s a useful tool for Windows Forms applications, it’s much less useful for Web application development.

Let’s look at a simple example. Suppose you use a DataSet to return a set of 1,000 products to display in a DataGrid on a form. Since you might want to sort or filter the data later, you choose to save the DataSet in a session variable. Not knowing any better, you also leave the default page ViewState turned on. When a user navigates to this page, there are three copies of the data somewhere in memory. It’s on the server saved in a session-level variable. It’s in the ViewState stored as the contents of the DataGrid. And it’s in the rendered HTML stream in the form of HTML table directives that render the table. Now multiply the server memory by the number of users to assess impact on server memory, and multiply the two copies of the data by the number of users to assess the impact on bandwidth utilisation. You can quickly overload a server and its available network bandwidth on a high-traffic site.

The answer: Use the DataReader
Though not as sexy, the DataReader is much more functional for a Web application. Because the DataSet object’s cursor is designed to iterate in a forward-only, read-only fashion over the results of a query, it’s very fast. Moreover, the DataReader only holds the current record in memory at any one time—never the entire results set. The DataSet object can be bound to ASP.NET Server Controls (like the DataGrid). More importantly, server resources and connection resources are released as soon as you’re finished traversing them. Build your data-bound pages using DataReaders to retrieve data from an underlying database whenever it’s important for the data to be as fresh as possible.

When should I use the DataSet?
The only time I recommend using the DataSet in a Web application is when the underlying data changes on an occasional basis. For example, if you have a series of drop-down boxes or checklist items that come from a database but rarely change, it may make sense to load them in a DataSet in the Application_OnStart event and put them into the cache so that any page that needs to get the values will have them immediately available. This not only makes data retrieval faster for each page but also minimises the number of hits to the underlying database. You can get an additional speed boost by caching the Web pages, which rely on the cached DataSet for their values.

By setting a dependency between the cached Web pages and the cached DataSet, the Web pages will be regenerated whenever the DataSet changes. To make sure the DataSet is always current, you should create Update, Insert, and Delete triggers on the tables in the DataSet that modify a control file on the site. Then set a dependency between the cached DataSet and the control file. Whenever the control file changes, the DataSet in the cache will be invalidated. Add code to the Session_OnStart event to check for the cached DataSet, regenerate it if necessary, and place it back in the cache. Then whenever the underlying tables change, the cached DataSet will be regenerated.

Using the right tool for the right job is the best way to create optimised Web applications. Now you have some general guidelines for the right time to use the DataReader and the DataSet in your ASP.NET applications.


May 12, 2008 Author: Ashish | Filed under: .Net

Advertisement

Adsense