The next "Version" of the web?
I have recently been having some debates on Silverlight /
AIRs importance to the web. Some of these people in the discussion often say
that AJAX enabled web sites are enough for what you need on the web. I whole heartedly
disagree with this assessment, so I thought I would make a blog posting about why I feel the importance of RIA development on the web is an important
next step for our view of the web in the coming years.
Recently, I would say the past 5 or so years, we have been
hearing a trendy word passed around called “Web 2.0”. To me, this merely means
socializing on the Web in new and exciting ways. Sites such as Digg, Twitter,
etc. have crafted a new age of our society and new ways for us to communicate.
This is a great step forward for our generation. However, many have started to
wonder what the next version of the Web will look like (“Web 3.0”).
I have a hypothesis of what will be more important to our
next version of the web, and it will be somewhat seamless to the typical user
that surfs the web. It will be a better organization of the domain layer,
meaning, it will be spreading out your business logic and domain layer across
multiple web services and Restful services that can allow for multiple consumption
of the users data. This will allow you to access the data through Desktop
application, Web applications, or consumption from other companies trying to
tie into your data. Now, from this definition, there are a lot of people who
are already there: Amazon, Ebay, Microsoft, etc. This is true, but once this
becomes the norm way of creating content on the web, I think it will be an
expected feature across all applications. To me the idea of “the next web
version” will be the multiple ways of getting to your data and consuming that
data.
Where does RIA come into this
All that being said, how does Silverlight and AIR play into
this. There is an excellent article I read from nikhilk.net that talks about
this: "http://www.nikhilk.net/Entry.aspx?id=190".
In it he describes the Reach vs Capabilities approach, where Classic Simple
HTML is the broader reach, then AJAX
apps, then AJAX + RIA (Silverlight and AIR), RIA apps, then Desktop apps that
finally goes to greater capability. I
think this is an accurate assessment of the technology at hand.
To me, Silverlight (and AIR) will be the new way to
present web controls and feature components that give some depth and control to
the user, in a presentation that has both speed and power. Silverlight gives
the same flexibility as a windows application where the separation of concerns
(domain objects) rests on your web services. It helps you really build out an enterprise
system that will force you to create the idea of the “next generation of the
web”. It will do this by really making you think how to structure your domain
objects in interesting ways. This will enable you to create your domain layer mechanisms
so that they can be consumed in multiple ways, such as an AJAX Website, Desktop
application, and the RIA application (Silverlight and AIR). During this process,
it will allow you to space out your business logic and domain logic across
multiple servers, but still provide a very quick interface for the client, as
well as maintain state storage locally and give the user a better environment
to manipulate their data.
It's not Active-X
One thing I keep hearing is Silverlight is the next
Active-X. It is really nothing like Active-X, except for the fact that it runs
on the client side. Active-X was a way for the client to consume a dll created
by the site and run that business logic locally to that client. Silverlight is a
complete framework that runs on the client’s side. The domain layer “should be”
created and utilized from the Server side (web services / Rest ful APIs). The
point here is that Active-X can be any user created DLL that is registered to
your browser and interacts with your OS. Silverlight is the partial .NET
Framework running on your system, it doesn’t interfere with the OS or register
any components to it. It is completely self contained, unlike Active-X. An
active-x object could wreak havoc on your system, where-as a Silverlight
application has restrictions on what it can do, because it is self contained in
the Framework for it. It is nothing more
or less than Flash, which is not considered Active-X in nature. What it is, is
a fraction of the .NET 3.5 Framework running locally on the client, with a
WPF/E (Windows Presentation Foundation Everywhere) front-end that provides
vector style graphics and a rich GUI and mechanisms to consume data in multiple
facets.
Whoa ... dont get carried away!
All that being said, I do not think having a majority of
your website in Silverlight is a good thing; far from it. Silverlight, to me,
is great for doing complex User controls and Components that require speed, complex
GUI, depth, and simplicity of use. I can foresee a lot of Admin
Sections being in Silverlight, as well as key complex components, such as
Events Calendar, Attendance components, Form Builder, etc. Really anything that
requires a complex GUI that requires simplicity and depth.
We all know that JavaScript is great for simple things,
but the more complex a page gets and the more complex the GUI needs to perform,
the more of a headache JavaScript can become for the typical developer. Not to
mention, JavaScript will result in a much slower interface. Looking back at all
of the issues I have ran into lately with several projects, over 35-40% of the
issues was trying to get Javascript and .NET to play nicely together. Now, if I
had been using ASP.NET MVC, a lot of those
problems could have been mitigated, however, the more complex your JavaScript
code is, the more cumbersome the code will get, and the more
difficult to maintain. I am not saying JavaScript is bad and convoluted, but it
is something to me that should be mitigated to making simple features seem more
alive.
Proof of concept
I am working on a proof of concept application that is
built on the ASP.NET MVC framework, that makes and
includes a User Control written in Silverlight (which is developed to use the
same MVC pattern), to complement and see how all the pieces fit together. I
read an article at http://marlongrech.wordpress.com/2008/03/20/more-than-just-mvc-for-wpf/
by Marlon, that I am very fascinated to try out in Silverlight. The article
talks about using the MVC + Mediator pattern to communicate between all of the
pieces in WPF. However, since Silverlight is a subset of WPF, and I cannot find
a way to do the EventManger and register events of a XAML view from another
class at the moment, I am having to do a mix between MVC + M and a little more
work on the view’s side to create events that the controller can consume. I am
trying to do as little code as possible on the Views side and keep it all
confined in the Controller side. It is coming along great, and I really
think this is the way to bring MVC in Silverlight with ASP.NET MVC Framework to
keep the separation consistent on both sides.
I am currently trying to write a lot of domain base
classes and interfaces to help speed up the connection of these two
technologies, to help mitigate the time spent including them in the same
project. I am also working on a "drag in and work" Security ASP
Membership feature for MVC ASP.NET by extending a
starter kit found at http://www.squaredroot.com/post/2008/04/MVC-Membership-Starter-Kit.aspx
by Troy to fit the needs that I typically use for the Memberships.
I think this is an important topic for all developers and managers to address. I believe this is what the future of the web will look like, come hell or high water. You will either be on the train, or trying to flag it down. Either way, it will be a wonderful ride.
Everyone likes examples
Before I leave off today, I would like to leave some links
to some good Silverlight Examples:
Quick Silverlight Examples:
MSPaint Type Example: http://silverlight.net/Samples/2b1/ImageSnipper/testpage.html
Awesome Weather Tool: http://silverlight.r2musings.com/weatherwidget/default.aspx
Google with a stylus or mouse: http://www.tabletpcpost.com/search/
Upload Tool: http://fluxtools.net/emailphotos/
Slide show Image App: http://www.vertigo.com/SlideShow.aspx.
some more neat ones at silverlight.net
Today I would like to discuss how to force LINQ to SQL to
load child objects, as well as shape its load, without deferred execution. Before we begin, let’s first discuss why that
is even necessary.
What does Deferred
execution give us:
Composable queries and deferred
execution work together to make LINQ a unusual rich query language. If you
properly understand these features of LINQ you will be able to write less code
that executes faster, in order to accomplish more.
What
problems can it create:
var
query = from customer in
db.Customers
where
customer.City == "Paris"
select customer;
foreach (var
Customer in query) << Query
Executes Customer here
{
Console.WriteLine(Customer.CompanyName);
foreach (var order
in Customer.Orders) << Query of
Orders Executes here for each loop
{
Console.WriteLine(order.OrderID);
}
}
As
you can see above, you are sending SQL across the wire each time you loop
through the Customer object. This is not an ideal way to handle this load if
you already know you will need to pre-load all of the child objects ahead of
time.
Forcing it to load up the child objects if
you know you are going to need them:
//Create a new
Data Context to load data objects
NorthwindDataContext nwind = new NorthwindDataContext();
//Create a
DataLoad Options object to tell the Datacontext
// how to load this object, if it
// loads the specified object (through the
generic part)
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Product>(p => p.Category);
options.LoadWith<Product>(p => p.Order_Details);
options.LoadWith<Order_Detail>(od => od.Order);
//Set the load
options for this dataContext
nwind.LoadOptions = options;
//This products
object has preloaded the Parent object Category
// and Child object OrderDetails,
// as well as each OrderDetails parent Order
object
IEnumerable<Product> products = nwind.Products.ToList<Product>();
Changing what data actually gets loaded for
the child objects for the Customer’s Order:
//Create a
DataLoad Options object to tell the Datacontext
// how to load this object, if it
// loads the specified object (through the
generic part)
DataLoadOptions newOptions
= new DataLoadOptions();
//This will tell
the Datacontext to load the Order Object
// for the Customer (when it loads through
deferred or not)
// to load only those Orders with OrderID <
10,700 and
// in descending order by OrderDate
newOptions.AssociateWith<Customer>(c => from
o in c.Orders
where
o.OrderID < 10700
orderby
o.OrderDate descending
select o);
As I hope you can see, LINQ to SQL gives you a lot of flexibility on how to
load and when to load. I really do enjoy working with LINQ and all its pieces.
I still have a lot to learn with it, but the more I use it the more I appreciate
it and become reliant on it.
As part of this blog, I plan to have an on-going set of
articles that takes a detailed look into some part of the .NET Framework. I plan
to bring as much knowledge as I can find on the topic, but drill down into a
subset of its components, to make aware and have discussions. This will bring
about more understanding for me and the .NET Community. So if you have
suggestions, comments, or a different idea on the subject, please share
it.
Before I begin on discussing its key components, I would like to
point you to some very good articles written about LINQ to SQL, which should be
read to have a deeper understanding of this new technology, and prepare you to
be able to use it comfortably. This is a brief listing of articles written to
help you better understand how to use LINQ to SQL:
Scott Gu’s Multi-part Tutorials:
Rick
Strahl has many articles written on practical usages
of LINQ
Pro
LINQ by Joseph C. Rattz, Jr. (a book I have read
cover to cover multiple times and actually reference some of his
explanations).
Today I would like to discuss some of LINQ to SQL’s key
components, specifically the DataContext and Entity
objects. I have read a lot of articles dealing on how to effectively use LINQ to
SQL, and how to structure it. However, I would like to drill down more into what
two very specific parts are and what they are responsible for. Understanding
these objects more fully and being aware of it, will help bring about
understanding and correct usage of these components.
Before we drill down into what the DataContext and Entity objects are and what they are
responsible for, let’s take a brief look at the key difference between LINQ to
SQL and LINQ to Objects / XML.
What
are the key differences between LINQ to SQL and LINQ to Objects /
XML?
- LINQ
to SQL needs a Data Context object (generic or a Custom Inherited version).
- LINQ
to SQL returns IQueryable<T>
LINQ to Objects
/ XML returns IEnumerable<T>
- Normal
LINQ queries are performed on arrays/collections that implement IEnumerable<T>.
- LINQ
to SQL queries are performed on classes that implement the TQueryable<T> interface, such as Table<T>.
- LINQ
to SQL is translated to SQL unlike LINQ to Objects / XML which are translated to
Intermediate Language (IL).
LINQ to SQL is translated to SQL by way of
Expression Trees, which allow them to be evaluated as a single unit and
translated to appropriate and optimal SQL Statements.
- Normal
LINQ is executed in local machine memory, LINQ to SQL
is translated to SQL calls and executed on the specified
Database.
What
are the similarities shared between LINQ to SQL and LINQ to Objects /
XML?
The
similarities shared between all aspects of LINQ are the concept of Deferred
Loading and Execution. I will not go into detail on this topic here, as it has
been discussed countless times on various other blog articles. Suffice it to
say, that it allows you define a LINQ query that maintains what you want to query, but doesn’t
actually query that item until you use it.
Quick
Example:
var
query = from customer in db.Customers
where customer.City ==
"Paris"
select customer;
foreach (var Customer in query)
<< Query Executes Customer
here
{Console.WriteLine(Customer.CompanyName);
foreach (var order in Customer.Orders) << Query of
Orders Executes here
{
Console.WriteLine(order.OrderID);
}
}
This
allows you to define what you query,
through various code paths, until you actually need to use the query for
data.
Example:
NorthwindDataContext northwind = new NorthwindDataContext();
var products = from p in northwind.Products
select p;
if
(somecondition)
{ products = from p in
products
where p.Discontinued
select
p;
}
foreach (Product p in
products)
{ // do something }
Responsibilities
of the Entity Classes and the DataContext Class
Let
us continue on to our discussion about what the DataContext and Entity objects responsibilities are and what
they actually do for us. We will discuss the core concepts of what the DataContext and the Entity classes do and a description of
how it does it. For the Entity class part, we will actually get down and dirty
with how it actually accomplishes its responsibilities, since we can visibly see
how it does it via the code generated for us.
Please
note, that all of this is automatically handled if you use the SQLMetal or OR/M designer in Visual Studio 2008. This
knowledge is to understand what it automatically provided to us, to better
understand these objects and also if we decided to implement these features
ourselves.
DataContext
class is responsible for identity tracking, change tracking, and change
processing.
All of this is automatically handled by the base DataContext
Class.
Identity Tracking:
When a
record is queried from the database for the first time since the instantiation
of the DataContext object, that record
is recorded in an identity table using its primary key, and an entity object is
created and stored in cache. Subsequent queries that determine that the same
record should be returned will check the identity table, and if the record
exists in the identity table, the already existing entity object will be
returned from the cache. This is an important concept to grasp, so I will
reiterate it in a slightly different way. When a query is executed, if a record
in the database matches the search criteria, and its entity object is already
cached, the already cached entity object is returned. This means that the actual
data returned by the query may not be the same as the record in the database.
The query determines which entities will be returned based on the data in the
database, but the DataContext’s identity tracking
service determines WHAT data is returned. Lucky for us, we can refresh the DataContext’s
cache and have it retain our changes. I will show an example of this
later.
Change Tracking:
Once the identity tracking service creates an entity object in its
cache, change tracking begins for that object. Change tracking works by storing
the original values of an entity object. Change tracking for an entity object
continues until you call the SubmitChanges method. Calling the
SubmitChanges method causes the
entity objects’ changes to be saved to the database, the original values to be
forgotten and the changed values to become the original values. This allows the
change tracking to start over. This works fine as long as the entity objects are
retrieved from the database. However, merely creating a new entity object by
instantiating it will not provide any identity or change tracking until the
DataContext
is aware of its existence. To make the DataContext aware, simply insert
the entity object into one of the Table<T> properties (that
represents the collection of the Table). To accomplish this just call InsertOnSubmit or
Attach method on the DataContext’s Table<T> property passing this
new entity instance as a parameter. When this is done, the DataContext will begin identity
and change tracking on that entity object.
Change
Processor:
When
you call SubmitChanges() method, the DataContext
object’s change processor manages the update to the database. First, the change
processor will insert any newly inserted entity objects to its list of tracked
entity objects. Next, it will order all changed entity objects based on
dependency. Then, if no transaction is in scope, it will create a transaction so
that all SQL commands carried out during this invocation of SubmitChanges
will have transactional integrity. It
uses SQL Server’s default isolation level of ReadCommited, which means that data read will not be
physically corrupted and only committed data will be read, but since the lock is
shared, nothing prevents the data from changing before the end of the
transaction. Lastly, it will enumerate through the ordered list of changed
entity objects, creating necessary SQL and executing them.
Entity
classes
are responsible for change notification, graph consistency, and implementing
good practices.
Change
Notification:
The
DataContext
must be able to monitor this Entity
class and know what changed and when it is changed. The Entity class must notify
the DataContext that something has changed, in some
form or fashion.
Graph
Consistency:
Updating
the relationship between two entity objects, such as Products and Orders. The
reference on each side of the relationship must be properly updated so that each
entity object refers to each other (or no longer refers if removed).
Implementing Good Practices:
Entity classes should implement INotifyPropertyChanging and INotifyPropertyChanged. If you
decide to make an entity class by hand, and do not implement this and change
notification, the DataContext will need to create 2
copies of each entity object: one with original copy, to compare and determine
changes (highly inefficient).
Add OnCreated to the constructor of
the entity class so that the DataContext knows it is created
and create partial methods of [PropertyName]Changing / [PropertyName]Changed for each property and add it before
and after each set of that property.
Examples showing how all these responsibilities work
Examples
of how it accomplishes change notification, and
graph consistency (as taken from the book Pro Linq):
[Column(Storage="_ShipCountry", DbType="NVarChar(15)")]
public string ShipCountry
{
get
{
return this._ShipCountry;
}
set
{
if ((this._ShipCountry != value))
{
this.OnShipCountryChanging(value);
this.SendPropertyChanging();
this._ShipCountry = value;
this.SendPropertyChanged("ShipCountry");
this.OnShipCountryChanged();
}
}
}
As
you can see in this example, the get part of this property is relatively simple.
When we take a look at the set part of this property, we can see it is calling
the INotifyPropertyChanging and INotifyPropertyChanged versions
of SendPropertyChanging and SendPropertyChanged. This is to let the DataContext know that this property is about to change and
has actually changed.
The
other two odd looking methods to notice is the OnShipCountryChanging and OnShipCountryChanged methods. This is the partial methods
that the user can attach to (if they decide to partial out this entity class) to
add validation or additional logic if they so choose. Partial methods are like
lightweight Event Handlers, in that you can stub them out in one partial class
definition, and actually define them in another partial class definition. But if
they are never defined in another partial implementation, the compiler removes
all traces that the partial method ever existed. So it is a very efficient way
to add functionality, and if you never use it, it isn’t added to the compiled
code – hence like a lightweight event.
public
Order()
{
this._Order_Details = new EntitySet<Order_Detail>(
new Action<Order_Detail>(this.attach_Order_Details),
new Action<Order_Detail>(this.detach_Order_Details));
this._Customer
= default(EntityRef<Customer>);
OnCreated();
}
private void attach_Order_Details(Order_Detail entity)
{
this.SendPropertyChanging();
entity.Order = this;
}
private void detach_Order_Details(Order_Detail entity)
{
this.SendPropertyChanging();
entity.Order = null;
}
This
part of the example sets up the relationship to the Order Entity object. It sets
up the child objects Order_Details, and its parent object Customer. The
interesting thing here to note is the fact that the parent object is of type
EntityRef<ParentObject> and the child object is of type
EntitySet<ChildObject>. EntityRef and EntitySet are generic types that
allow the deferred loading to occur. Meaning, they simply state what the
relationships are, but do not actually load the references, until it is
absolutely necessary. The main difference is EntityRef is a single object that
represents the fact that it is a parent and EntitySet is a collection of objects
that represents a set of children.
You can see above in this example that
EntitySet<Order_Detail> gets instantiated with two Action delegates. This
is to specify how to attach and how to detach. It will use these mechanisms to
add the children and remove the children to this relationship. Adding will take
place when you manually add a new or existing Order_Detail object to this Order
object, and remove will occur when you Remove it. This setup is there because to
remove or add a relationship, requires it be done on two sides. If you are
adding a child object to a parent object (such as adding a Order_Detail object
instance to an Order object), you must tell both the Order_Detail object that it
is now a child of the Order object (setting its parent), and tell the Order
object that it now contains a new child object of that Order_Detail instance.
The above example deals with this new child object and how to attach to this
parent object (as you can see in the attach_Order_Details method above).
Simply put, LINQ to SQL will use these two action delegates to assign a
Order to an Order_Detail, or remove an assignment of Order from Order_Detail.
This and the previous example is how Change Notification works and functions in
LINQ to SQL, and how the DataContext knows what to update, remove, and
insert.
[Association(Name="Order_Order_Detail", Storage="_Order_Details", OtherKey="OrderID")]
public EntitySet<Order_Detail>
Order_Details
{
get
{
return this._Order_Details;
}
set
{
this._Order_Details.Assign(value);
}
}
In
the Order_Detail (children reference) example above,
it simply returns out the EntitySet collection of the
children objects and maintains the deferred execution until you absolutely need
it. When the child objects are needed, it executes the relevant SQL and grabs
the child objects. It then calls the set part of this property which re-assigns
the EntitySet with the loaded child
objects.
[Association(Name="Customer_Order", Storage="_Customer", ThisKey="CustomerID", IsForeignKey=true)]
public Customer Customer
{
get
{
return this._Customer.Entity;
}
set
{
Customer previousValue = this._Customer.Entity;
if (((previousValue != value)
|| (this._Customer.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Customer.Entity = null;
previousValue.Orders.Remove(this);
}
this._Customer.Entity = value;
if ((value != null))
{
value.Orders.Add(this);
this._CustomerID = value.CustomerID;
}
else
{
this._CustomerID = default(string);
}
this.SendPropertyChanged("Customer");
}
}
}
In
the Customer (parent reference) example, we will skip the get part of the
Property as it is fairly obvious what it is doing and focus on the set part.
Customer
previousValue = this._Customer.Entity;
You can see that the first line of the set
method code, they store off a copy of the original Customer assigned. Don’t let
the fact that it is calling Entity on the _Customer member confuse you.
_Customer is of type EntityRef<Customer>, so in
order to get the actual customer, we actually have to
call directly to it.
if
(((previousValue != value)
|| (this._Customer.HasLoadedOrAssignedValue == false)))
The
above statement is checking to see if the Customer is currently being assigned
to an existing customer. If it is the same customer that is already assigned,
there is nothing more to do.
this.SendPropertyChanging();
As
part of the change tracking system, this is notifying the DataContext that
we
are about to change the Customer Property as part of the Change Tracking /
Notification piece.
if
((previousValue != null))
{
this._Customer.Entity
= null;
previousValue.Orders.Remove(this);
}
This
next part of the code determines if the previous Customer object is null. If it
isn’t null, then clear out the relationship between the previous parent and this
child object. The inner two lines simple removes the parent object reference and
the child reference to this object (in the previous parent). Calling the Remove
method above will cause the Customer class’s detach_Orders (similar to the top example) to get called and
the passed Order object to be removed. In the detach_Orders method, the passed Order object’s Customer
property is set to null and looks like the following:
private void detach_Orders(Order entity)
{
this.SendPropertyChanging();
entity.Customer = null;
}
As
you can see, when the Customer property is set to null, this will cause the
Order object’s Customer property’s set method to be called, which is the method
that invoked the code that called the detach_Orders
method. So the very method that started this process of removal is getting
called recursively.
set
{
Customer previousValue = this._Customer.Entity;
if
(((previousValue != value)
|| (this._Customer.HasLoadedOrAssignedValue == false)))
Remember
how we set the Customer parent object to null before we called the detach_Order method? Well, because of this, the previousValue is set to null, and since we are passing in
null, stops the execution here without doing anything
else.
So,
once the recursion call to the set method returns, we no longer have a reference
between the prior parent and that parent to this child object. We are now back to the next line of our code.
this._Customer.Entity
= value;
if ((value != null))
{
value.Orders.Add(this);
this._CustomerID = value.CustomerID;
}
else
{
this._CustomerID = default(string);
}
The
above first line sets the new parent object to the _Customer member to maintain
the parent relationship. The next line will check the value to make absolutely
sure it is not null. If it was null, it would just assign the default value. If
it isn’t null, we will set the new parents child reference to point to this
object. The current Order object will be passed to the Customers collection of
child Order objects. If it was null, it would just assign the default
value.
The result of this will cause the attach_Order method to be called. This will assign the
current Order object’s Customer object to the passed Customer, resulting in the
Order object’s Customer property’s set part being called again (the second part
of the recursion).
if
(((previousValue != value)
|| (this._Customer.HasLoadedOrAssignedValue == false)))
Just
like previous, this line will break the recursion. Remember “this._Customer.Entity
= value;”, before
our recursion out, we set the Oreder object’s Customer
property to the new Customer, who was passed this set part again from the attach_Orders method. Since they are the same, this exits
out the recursion the same way the detach
did.
The last thing of relevance is setting the Customer ID from the new
Customer parent, and the letting the DataContext know
that this property was changed via the this.SendPropertyChanged("Customer")
part.
This whole system is
required to maintain a one-to-many relationship, between the Entity objects and
maintain the graph consistency between them. If you decide to write an Entity
class by hand, you must remember to implement a feature similar to this, as the
Entity class is responsible for its own graph consistency. A typical approach
(if you really want to make your own entity class), is to allow the ORM create
these classes for you, and copy the code and paste it into your own Entity
class. This will cut down on the amount of work you need to do to maintain
change tracking and graph consistency.
It
may take a couple of re-reads to fully get what is accomplished here, but it
will be worth it to fully understand how all of the change tracking and graph
consistency “magic” occurs. If you truly want a deeper understanding of these
concepts, I highly recommend reading the book “Pro LINQ” I mentioned earlier. It
opened my eyes to a lot of the inner workings of LINQ in general.
Some of
the key components not mentioned in this article is the
concept of Expression Trees. This is essential to how LINQ to SQL works. In
future blog articles I will try to get more in depth with this concept, and how
they relate to the .NET Framework 3.5 as a whole. For now I highly recommend
reading the article posted here. Another thing I would like to touch on in the
future is the inner workings of the DataContext, and
how it accomplishes the responsibilities.