Archive for June, 2008

Managed Extensibility Framework (MEF) at a Glance

June 16th, 2008 by Sidar Ok

After Krysztof Cwalina has announced Microsoft’s plans on releasing extensible features to .NET framework, the CTP of MEF has made its way too quick into the market. As a result of this, we have an immature .DLL called ComponentModel.dll born into our hands, with being far from complementing the community’s needs and lacking lots of features & architectural concerns – and of course you know it, with nearly no xml comments and not very informative error messages.

But it is still CTP, and that’s what a CTP is for. Criticism against it is a matter of another post; but as .NETters we need to know about this vibe coming because this young baby is going to be a part of the core framework and one day, with an update, will be pushed to x million of computers.

So I better stop judging for the time being and let’s get our hands dirty with what we have currently as early adopters.

Dependency Injection with Management Extensibility Framework

In the dll, there are 2 main namespaces coming: System.ComponentModel.Composition and System.Reflection.StructuredValues . So as you see, to do its magic MEF uses reflection. This also means that one can define contracts using hard coded strings instead of a strongly typed contract. In the examples those are shipped with MEF, you can see this usage, but I think everybody will agree that this is not a good practice. So let’s see how we can build a strongly typed and contract based dependency injection mechanism.

So let’s get to define a very simple contract:

   1: /// <summary>
   2: /// Contract for retrieving pretty dummy data
   3: /// </summary>
   4: public interface IDataRetriever
   5: {
   6:     /// <summary>
   7:     /// Gets the sample data.
   8:     /// </summary>
   9:     /// <param name=”count”>The count.</param>
  10:     /// <returns></returns>
  11:     IEnumerable<ExampleData> GetSampleData(int count);
  12: }

This expects to get a list of example data #count times. ExampleData is a Poco, and its structure contains just a string key:

   1: /// <summary>
   2: /// Model for Sample Data
   3: /// </summary>
   4: public class ExampleData
   5: {
   6:     /// <summary>
   7:     /// Gets or sets the data key.
   8:     /// </summary>
   9:     /// <value>The data key.</value>
  10:     public string DataKey
  11:     {
  12:         get;
  13:         set;
  14:     }
  15: }

So we expect from this method to return a list of ExampleData , with their DataKey field populated by their indexes. So here is the test that ensures this basic expectation:

   1: /// <summary>
   2: ///A test for GetSampleData
   3: ///</summary>
   4: [TestMethod()]
   5: public void GetSampleDataTest()
   6: {
   7:     IDataRetriever target = new DataRetriever();
   8:     int expectedCount, count;
   9:     expectedCount = count = 10;
  10:
  11:     IEnumerable<ExampleData> actual = target.GetSampleData(count);
  12:     Assert.AreEqual(expectedCount, actual.Count<ExampleData>());
  13:
  14:     IEnumerator<ExampleData> enumerator = actual.GetEnumerator();
  15:     int expectedKey = 0;
  16:     while (enumerator.MoveNext())
  17:     {
  18:         Assert.IsNotNull(enumerator.Current);
  19:         Assert.AreEqual(enumerator.Current.DataKey, expectedKey.ToString());
  20:         expectedKey++;
  21:     }
  22: }

And after a couple of failures (yes, even in this simplicity I manage to fail), here is the implementation that passes this test:

   1: [Export(typeof(IDataRetriever))]
   2: public class DataRetriever : IDataRetriever
   3: {
   4:     #region IDataRetriever Members
   5:
   6:     //[Export(”Retriever”)]
   7:     public IEnumerable<ExampleData> GetSampleData(int count)
   8:     {
   9:         List<ExampleData> retVal = new List<ExampleData>(count);
  10:         for (int i = 0; i < count; i++)
  11:         {
  12:             yield return new ExampleData()
  13:             {
  14:                  DataKey = i.ToString()
  15:             };
  16:         }
  17:     }
  18:
  19:     #endregion
  20: }

Now, the syntax of MEF needs us to shout what we have, and explicitly define by attributes what we want to expose as our services to be injected (intrusive, girrrr…) And since there is an exporter, there should be an importer too, which is a page in this scenario. Beware, the page is using this interface and it is marked as public, as MEF can inject only public dependencies choosing the same way as the many of the other IOC containers in the wild.

   1: [Import(typeof(IDataRetriever), IsOptional = false)]
   2: public IDataRetriever Retriever
   3: {
   4:   get;
   5:   set;
   6: }

Note that these Import and Export attributes are under System.ComponentModel.Composition namespace and they both have another overload that takes strings as contract names instead of contract types as shown above.

Please also note that as a client to DataRetriever, this page doesn’t know any bit about which implementation of IDataRetriever that it will retrieve (DI mission accomplished). So in which house is all the party happening? I placed an initialization code inside the page constructor:

   1: public _Default()
   2: {
   3:     DataRetrieverHelper.InitializeContainer<_Default>(this);
   4: }

And this helper is a very smart guy who knows about everything about this magic (so from what we learnt from Italian mafia movies, it should be killed – by a DSL or an XML configuration. But MEF doesn’t support it currently out of the box – but with a bit of a hack it can be done):

   1: public static class DataRetrieverHelper
   2: {
   3:     public static void InitializeContainer<T>(T toFillDependency)
   4:         where T:class
   5:     {
   6:         CompositionContainer container = new CompositionContainer();
   7:         container.AddComponent<T>(toFillDependency);
   8:         container.AddComponent<DataRetriever>(new DataRetriever());
   9:         container.Bind();
  10:     }
  11: }

As you see, you add the consumer, add the service, and call bind – MEF container cares the rest.

Of course, the first question that is expected after how, is what if we need to add another implementation which is a very likely scenario? For e.g what if we have 2 implementations of the contract, say to return a list of keys in reverse order and the normal one, which one is the container going to choose to bind?

Handling Multiple Exports Within the Container

Well, since the requirements are extended, we need to write another test for the new requirement:

To pass this test, the implementation is trivial:

   1: /// <summary>
   2: ///A test for GetSampleData
   3: ///</summary>
   4: [TestMethod()]
   5: public void GetSampleDataReverseTest()
   6: {
   7:     IDataRetriever target = new DataRetreiverReverse();
   8:
   9:     int expectedCount, count;
  10:     expectedCount = count = 10;
  11:
  12:     IEnumerable<ExampleData> actual = target.GetSampleData(count);
  13:     Assert.AreEqual(expectedCount, actual.Count<ExampleData>());
  14:
  15:     IEnumerator<ExampleData> enumerator = actual.GetEnumerator();
  16:     int expectedKey = count - 1;
  17:     while (enumerator.MoveNext())
  18:     {
  19:         Assert.IsNotNull(enumerator.Current);
  20:         Assert.AreEqual(enumerator.Current.DataKey, expectedKey.ToString());
  21:         expectedKey–;
  22:     }
  23: }

It is obvious that if we don’t change the helper class which does the magic, we won’t get the new implementation. So let’s add it by using AddComponent generic method :

   1: public static void InitializeContainer<T>(T toFillDependency)
   2:     where T:class
   3: {
   4:     CompositionContainer container = new CompositionContainer();
   5:     container.AddComponent<T>(toFillDependency);
   6:     container.AddComponent<DataRetriever>(new DataRetriever());
   7:     container.AddComponent<DataRetreiverReverse>(new DataRetreiverReverse());
   8:     container.Bind();
   9: }

Ok, let’s run the application, and face a very nice error message:

“There was at least one composition issue of severity level ‘error’. Review the Issues collection for detailed information”

“WTF is issues collection?” were the first words out of my mouth unfortunately :) . The exception we get here is a System.ComponentModel.Composition.CompositionException and the “issues” is the Issues property of the exception we are getting. This is a collection of System.ComponentModel.Composition.CompositionIssue object, and their Description field is a string that has the meaningful explanation of what’s happening. In the list I got there was 2 issues that we were already expecting:

  1. “Multiple exports were found for contract name ‘MEFSample.Interfaces.IDataRetriever’. The import for this contract requires a single export only.”
  2. “A failure occurred while trying to satisfy member ‘Retriever’ on type ‘default_aspx’ while trying to import value ‘MEFSample.Interfaces.IDataRetriever’. Please review previous issues for details about the failure.”

Apart from not getting the exceptions in the first go, and ignoring the first message is cryptic, well, this is nice. Theoretically I can see all the errors happened during the build up process, not get stuck in the first one.

Back to the game, now I have 2 implementations in the container, I need a mechanism to choose between two – There is where this System.ComponentModel.Composition.ImportInfoCollection comes into the game. This collection holds a list of ImportInfo objects, which are basically information about the injected members, nothing more. Now, the new property will go as follows:

   1: [Import(typeof(IDataRetriever), IsOptional = false)]
   2: public ImportInfoCollection<IDataRetriever> ResolvedDependencies
   3: {
   4:     get;
   5:     set;
   6: }

When “Bind” is called, this property will be filled automatically instead of the old one. So now I have a list, I am able to decide between the resolved dependencies. Here is my new Retriever property:

   1: public IDataRetriever Retriever
   2: {
   3:    get
   4:    {
   5:        return ResolvedDependencies[0].GetBoundValue();
   6:    }
   7: }

Here I am choosing the first one; I can choose the 2nd one as well since Resolved Dependencies collection will have 2 values.Okay, [0] seems a cumbersome way of selecting the “correct” one, admitted :). Frankly, MEF team included the mechanism in this CTP so we can also specify metadata information along with the injected interfaces, which will help us to be able to decide better what implementation to choose in the run time. But this post got long so I hopefully throw another post to explore what we can do within MEF boundaries.

You can download the sources by clicking here.

kick it on DotNetKicks.com

Share it on: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogosphere News
  • e-mail
  • YahooMyWeb
  • DotNetKicks
  • DZone

Linq to SQL Wish List

June 4th, 2008 by Sidar Ok

As we are not Microsoft marketers, we tend to see the cons of the products that Microsoft builds. As every product has its flaws, of course Linq to SQL is no exception. Here is my list of the things that I compiled from various sites & forums or hit a limitation block by myself:

A) Architecture

1 – Enabled Provider Model & More providers than SQL

Since Matt Warren has explained in his blog post that LINQ to SQL supported multiple providers internally, but it was disabled for some “non-technical” reasons there is an unstoppable desire in each of us to see it enabled in the next version. (I am not even asking for Persistance Ignorance)

2 – Fully Mockable, A Design by Contract Framework

If this happened, we wouldn’t look for hacks like this one. I want to be able to mock DataContext out without doing any funky tweaks.

3 – A Disconnected Data Context

I can not remember how many times I have seen this everywhere. This DataContext will be able to be serialized & deserialized somewhere and even if it is dead, we should be able to benefit from Object Tracking and Deferred Loading.

By this I mean a stateless DAL, where I don’t have to say “delete these children” or “update these but not these”.

4 – Support for more inheritance models

Currently only Table per Hierarchy model is enabled, multiple entities constituting 1 table or vice versa are not.

5 – Out of the box many to many relationships

Title explains, as we currently can’t do this mapping.

6 – Batch Statements Execution

Currently Linq to SQL sends multiple queries to the DB if an operation needs it. A batch statement like NHibernate’s would have been more than cool.

7 – More control on the resulting statement

Advanced users should be able to sneak in the generation or submission process – like the interceptors in NHibernate again.

B) Tools and Designer

1 – Code generation into different structures

The ability to separate each entity and DataContext into different files or assemblies. Partial files do not let us reside our extensions in different assemblies.

2 – Make DBML designer support giving Entity Base Class

SQL Metal has this, so why does the designer not?

3 – Make DBML designer support external mapping

Again, this is a SQL metal specific “magic”.

4 – Enable partial generation in SQL Metal

Sometimes we human beings do not want to generate all the database, just one table for instance.

This is usually followed by a request on being able to “refresh” an object on the design surface. I don’t know anybody who fancies to delete & drop from connection explorer each time something is changed in the db and loose custom associations.

It would also be good if user changes to designer are kept, not overridden everytime by the tool (a smart merge may be?).

5 – A tool to generate POCO translators from/to Linq Entities

This could be configured in code or via XML files. Some of us (including me) are using Software Factories to generate them; it would have been nice to have an out of the box support in Visual Studio.

That’s all I can remember of at the midnight. What would you like to have in this list apart from these?

kick it on DotNetKicks.com

Share it on: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogosphere News
  • e-mail
  • YahooMyWeb
  • DotNetKicks
  • DZone

Linq to SQL with WCF in a Multi Tiered Action - Part 2

June 2nd, 2008 by Sidar Ok

In the first part of this article, I tried to define a Users & Favorites scenario and the things to keep in mind about Linq to SQL. In this post I’ll continue building that application and show its implementation in different tiers connected with WCF.

Here are the sources for the article.

Service Layer Design (Cont’d from Part 1)

Service Host (Web Service in our case)

This is a host project (a plain Web project) needed to host our web service. It has our .svc files and needed configuration. In .svc file we have the mapping from contract to the implementation:

<%@ ServiceHost Language=”C#” Debug=”true” 
Service=”ServiceImplementations.UsersService” %>

And the endpoint configuration goes as follows:

   1: <system.serviceModel>
   2:         <behaviors>
   3:             <serviceBehaviors>
   4:                 <behavior name=”FavoritesServiceBehavior”>
   5:                     <serviceMetadata httpGetEnabled=”true” />
   6:                     <serviceDebug includeExceptionDetailInFaults=”false” />
   7:                 </behavior>
   8:                 <behavior name=”UsersServiceBehavior”>
   9:                     <serviceMetadata httpGetEnabled=”true” />
  10:                     <serviceDebug includeExceptionDetailInFaults=”false” />
  11:                 </behavior>
  12:             </serviceBehaviors>
  13:         </behaviors>
  14:         <services>
  15:             <service behaviorConfiguration=”FavoritesServiceBehavior”
  16: name=”ServiceImplementations.FavoritesService”>
  17:                 <endpoint address=”" binding=”wsHttpBinding”
  18: name=”IFavoritesService_Endpoint”
  19:                     contract=”ServiceContracts.IFavoritesService”>
  20:                     <identity>
  21:                         <dns value=”localhost” />
  22:                     </identity>
  23:                 </endpoint>
  24:             </service>
  25:             <service behaviorConfiguration=”UsersServiceBehavior”
  26: name=”ServiceImplementations.UsersService”>
  27:                 <endpoint address=”" binding=”wsHttpBinding”
  28: name=”IUsersService_Endpoint”
  29:                     contract=”ServiceContracts.IUsersService”>
  30:                     <identity>
  31:                         <dns value=”localhost” />
  32:                     </identity>
  33:                 </endpoint>
  34:             </service>
  35:         </services>
  36:     </system.serviceModel>

Service Clients (Consumers)

The client layer is a very thin façade to invoke the requested methods from the channel. Clients are meant to be called through controllers if you are using MVC, and in our case our web application will consume the service so the endpoint configurations will live in web tier:

   1: <system.serviceModel>
   2:     <client>
   3:       <endpoint binding=”wsHttpBinding” bindingConfiguration=”"
   4: contract=”ServiceContracts.IFavoritesService”
   5: address=”http://localhost/WebServiceHost/FavoritesService.svc”
   6:         name=”FavoritesClient”>
   7:         <identity>
   8:           <dns value=”localhost” />
   9:           <certificateReference storeName=”My” storeLocation=”LocalMachine”
  10:             x509FindType=”FindBySubjectDistinguishedName” />
  11:         </identity>
  12:       </endpoint>
  13:       <endpoint binding=”wsHttpBinding” bindingConfiguration=”"
  14: contract=”ServiceContracts.IUsersService”
  15: address=”http://localhost/WebServiceHost/UsersService.svc”
  16:         name=”UsersClient”>
  17:         <identity>
  18:           <dns value=”localhost” />
  19:           <certificateReference storeName=”My” storeLocation=”LocalMachine”
  20:             x509FindType=”FindBySubjectDistinguishedName” />
  21:         </identity>
  22:       </endpoint>
  23:     </client>
  24: </system.serviceModel>

Presentation

The challenge in the presentation tier is we need to maintain the state of the each entity according to the user interaction. For this purpose, I put 2 GridViews , one for Users and One for favorites to enable insert, update, delete and select operations.

We will bind strongly typed collections (IList<User> and IList<Favorite>) to our GridViews and we will use the ID fields of the objects to associate with the gridview, and then use them in the code behind:

Here is the definition for Users GridView:

   1: <asp:GridView ID=”usersGrid” runat=”server”
   2:     AutoGenerateColumns=”False” CellPadding=”4″
   3:     ForeColor=”#333333″ GridLines=”None”
   4:     DataKeyNames=”UserId”
   5:     OnRowDeleting=”usersGrid_RowDeleting”
   6:     OnRowUpdating=”usersGrid_RowUpdating”
   7:     OnSelectedIndexChanged=”usersGrid_SelectedIndexChanged”
   8:     OnSelectedIndexChanging=”usersGrid_SelectedIndexChanging”
   9:     OnRowCancelingEdit=”usersGrid_RowCancelingEdit”
  10:     OnRowEditing=”usersGrid_RowEditing”>
  11:     <RowStyle BackColor=”#F7F6F3″ ForeColor=”#333333″ />
  12:     <Columns>
  13:         <asp:CommandField ShowDeleteButton=”True” />
  14:         <asp:TemplateField HeaderText=”First Name”>
  15:             <ItemTemplate>
  16:                 <asp:Label ID=”firstNameLabel” runat=”server”
  17: Text=’<%# Bind(”FirstName”) %>’></asp:Label>
  18:             </ItemTemplate>
  19:             <EditItemTemplate>
  20:                 <asp:TextBox ID=”firstNameTextBox” runat=”server”
  21: Text=’<%# Bind(”FirstName”) %>’></asp:TextBox>
  22:             </EditItemTemplate>
  23:         </asp:TemplateField>
  24:         <asp:TemplateField HeaderText=”Last Name”>
  25:             <ItemTemplate>
  26:                 <asp:Label ID=”lastNameLabel” runat=”server”
  27: Text=’<%# Bind(”LastName”) %>’></asp:Label>
  28:             </ItemTemplate>
  29:             <EditItemTemplate>
  30:                 <asp:TextBox ID=”lastNameTextBox” runat=”server”
  31: Text=’<%# Bind(”LastName”) %>’></asp:TextBox>
  32:             </EditItemTemplate>
  33:         </asp:TemplateField>
  34:         <asp:CommandField ShowEditButton=”True” />
  35:         <asp:CommandField ShowSelectButton=”True” />
  36:     </Columns>
  37:     <FooterStyle BackColor=”#5D7B9D” Font-Bold=”True” ForeColor=”White” />
  38:     <PagerStyle BackColor=”#284775″ ForeColor=”White” HorizontalAlign=”Center” />
  39:     <SelectedRowStyle BackColor=”#E2DED6″ Font-Bold=”True” ForeColor=”#333333″ />
  40:     <HeaderStyle BackColor=”#5D7B9D” Font-Bold=”True” ForeColor=”White” />
  41:     <EditRowStyle BackColor=”#999999″ />
  42:     <AlternatingRowStyle BackColor=”White” ForeColor=”#284775″ />
  43:     </asp:GridView>

The one for Favorites is pretty much the same so I’ll go over Users grid.

Let’s go to code behind which is more important to us. We are going to do a batch update and send List of Users, and each user in the list will have their favorites. All the entities will have their latest status in their Status field.

Here is a sequence diagram to make things easier and more clearer to understand :

image

Picture 1. Sequence diagram of what’s happening

Now, in the page load, we are going to populate the Users GridView:

   1: if (!IsPostBack)
   2: {
   3:    try
   4:    {
   5:        if (SessionStateUtility.Users == null)
   6:        {
   7:            // error may occur during disposal, not caring for the time being
   8:            using (UsersClient client = new UsersClient())
   9:            {
  10:                SessionStateUtility.Users = client.GetAllUsers().ToList<User>();
  11:            }
  12:        }
  13:        BindUsersGrid(SessionStateUtility.Users, -1);
  14:    }
  15:    catch (Exception ex)
  16:    {
  17:        Response.Write(ex.ToString());
  18:    }
  19: }

In the grid, user can update and delete users from session. For insert, there is a separate panel included at the bottom with an add button. In the add button what we are doing is quite simple, just adding a new user to the session:

   1: protected void addUserButton_Click(object sender, EventArgs e)
   2: {
   3:     Debug.Assert(sender != null);
   4:     Debug.Assert(e != null);
   5: 
   6:     User u = new User()
   7:     {
   8:         FirstName = firstNameTextBox.Text,
   9:         LastName = lastNameTextBox.Text,
  10:         EMail = emailTextBox.Text,
  11:         Status = EntityStatus.New,
  12:         UserId = SessionStateUtility.NextUserId,
  13:     };
  14: 
  15:     SessionStateUtility.Users.Add(u);
  16: 
  17:     BindUsersGrid(SessionStateUtility.Users, -1);
  18: }

You’ll notice 2 things here, one of them is the Status is set to Entity Status.New . The other one is the SessionStateUtility class. This acts as a provider and a helper for User lists. The Users list that it provides is the below:

   1: /// <summary>
   2: /// Gets or sets the users.
   3: /// </summary>
   4: /// <value>The users.</value>
   5: public static List<User> Users
   6: {
   7:     get
   8:     {
   9:         Debug.Assert(HttpContext.Current != null);
  10:         Debug.Assert(HttpContext.Current.Session != null);
  11:
  12:         return HttpContext.Current.Session[“Users”] as List<User>;
  13:     }
  14:     set
  15:     {
  16:         Debug.Assert(HttpContext.Current != null);
  17:         Debug.Assert(HttpContext.Current.Session != null);
  18: 
  19:         HttpContext.Current.Session[“Users”] = value;
  20:     }
  21: }

And it provides another method to get NextUserId. This is necessary because since there can be multiple new records in the screen, we will need to identify them. Next User Id brings the next highest negative number that is available:

   1: /// <summary>
   2: /// Gets the next id.
   3: /// </summary>
   4: /// <value>The next id.</value>
   5: public static int NextUserId
   6: {
   7:    get
   8:    {
   9:        if (SessionStateUtility.Users.Count == 0)
  10:        {
  11:            return -1;
  12:        }
  13:        int minId = SessionStateUtility.Users.Min<User>(user => user.UserId);
  14:
  15:        if (minId > 0)
  16:        {
  17:            return -1;
  18:        }
  19: 
  20:        return –minId;
  21:    }
  22: }

And then we need to handle the grid events. I wrote a helper function to Get the User object from Selected row index in the grid (it retrieves from session)”

   1: private User GetUserFromRowIndex(int index)
   2: {
   3:     int userId = usersGrid.DataKeys[index].Value as int? ?? 0;
   4: 
   5:     //retrieve the instance in the session
   6:     User user = SessionStateUtility.Users.Single<User>(usr => usr.UserId == userId);
   7:     return user;
   8: }

Another helper function is there for just to get user’s full name formatted:

   1: private string GetFullNameForUser(User u)
   2: {
   3:     return String.Format(CultureInfo.InvariantCulture, “{0} {1}”, u.FirstName, u.LastName);
   4: }

And this one updates the UI fields for a selected user:

   1: private void UpdateUiForUser(User u)
   2: {
   3:    if (u != null)
   4:    {
   5:        favoritesPanel.Visible = true;
   6:        userNameLabel.Text = GetFullNameForUser(u);
   7:        BindFavoritesGrid(u.Favorites.ToList<Favorite>(), -1);
   8:    }
   9: }

And of course one method for binding the grid:

   1: private void BindUsersGrid(IList<User> users, int editIndex)
   2: {
   3:     usersGrid.DataSource = users
   4:     .Where<User>(usr=>usr.Status != EntityStatus.Deleted);// only bind non deleted ones
   5:     usersGrid.EditIndex = editIndex;
   6:     usersGrid.DataBind();
   7: }

As you can see we are not binding the deleted ones but we are still keeping them in the session because we need to know what is deleted when we send them back to the data tier.

Then within the light of these methods, here goes the SelectedIndex_Changing event handler. It updates the favorite’s grid for the selected user:

   1: protected void usersGrid_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
   2: {
   3:     Debug.Assert(sender != null);
   4:     Debug.Assert(e != null);
   5:     usersGrid.SelectedIndex = e.NewSelectedIndex;
   6: 
   7:     User u = GetUserFromRowIndex(e.NewSelectedIndex);
   8:     UpdateUiForUser(u);
   9: }

And when the row is being edited, following event handler will get executed:

   1: protected void usersGrid_RowEditing(object sender, GridViewEditEventArgs e)
   2: {
   3:     Debug.Assert(sender != null);
   4:     Debug.Assert(e != null);
   5:
   6:     usersGrid.SelectedIndex = e.NewEditIndex;
   7: 
   8:     BindUsersGrid(SessionStateUtility.Users, e.NewEditIndex);
   9: }

And after user clicks edit, when he/she clicks update following handler will run:

   1: protected void usersGrid_RowUpdating(object sender, GridViewUpdateEventArgs e)
   2: {
   3:     Debug.Assert(sender != null);
   4:     Debug.Assert(e != null);
   5: 
   6:     int userId = usersGrid.DataKeys[e.RowIndex].Value as int? ?? 0;
   7:     //retrieve the instance in the session
   8:     User user = SessionStateUtility.Users.Single<User>(usr => usr.UserId == userId);
   9:     user.FirstName = (usersGrid.Rows[e.RowIndex].FindControl(“firstNameTextBox”)
  10: as TextBox).Text;
  11:     user.LastName = (usersGrid.Rows[e.RowIndex].FindControl(“lastNameTextBox”)
  12: as TextBox).Text;
  13:
  14:     user.Status = user.Status == EntityStatus.New ?
  15: EntityStatus.New :EntityStatus.Updated; // manage the state
  16: 
  17:     BindUsersGrid(SessionStateUtility.Users, -1);// back to plain mode
  18: }

As you see if the edited users’ current status is already new, then we are not modifying it. But else, the state is changed to the updated.

A similar situation also exists for deletion. Have a look at the handler below:

   1: protected void usersGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
   2: {
   3:     Debug.Assert(sender != null);
   4:     Debug.Assert(e != null);
   5: 
   6:     User user = GetUserFromRowIndex(e.RowIndex);
   7:     // If user is new and deleted now, we shouldnt send it over the wire again
   8:     if (user.Status == EntityStatus.New)
   9:     {
  10:         SessionStateUtility.Users.Remove(user);
  11:     }
  12:     else
  13:     {
  14:         user.Status = EntityStatus.Deleted;
  15:     }
  16: 
  17:     BindUsersGrid(SessionStateUtility.Users, -1);// back to plain mode
  18: }

We have done our work as a presentation layer, and we are now sending all the data through the service to data layer along with all the information needed for it to manage the generation of the SQL Statements (fingers crossed)

Data Layer Design

Since we are going to implement the IUsersDataAccess contract, we need to implement 4 methods: But I’ll focus on 2 of them especially. First one is GetAllUsers:

   1: /// <summary>
   2: /// Gets all users.
   3: /// </summary>
   4: /// <returns>The list of all users along with their favorites.</returns>
   5: public IList<User> GetAllUsers()
   6: {
   7:     using (FavoritesEntitiesDataContext context = new FavoritesEntitiesDataContext())
   8:     {
   9:         DataLoadOptions options = new DataLoadOptions();
  10:         options.LoadWith<User>(u => u.Favorites);
  11: 
  12:         context.LoadOptions = options; // load with favorites
  13:         context.ObjectTrackingEnabled = false; // retrieving data read only
  14: 
  15:         return context.Users.ToList<User>();
  16:     }
  17: }

As you see, we are telling the context to load every user with their favorites. This can cause some damage if these tables are very big, and there are methods to enhance this experience.

The UpdateUsers(IList) method is a bit more complicated. Here are the list of things that we are going to do:

  • Attach the users to the context who have their status “Updated’ – obvious one

  • Attach the users to the context who have their status “Deleted’ – since the context does not know about an object that is not attached, we need to attach them too.

  • We aren’t going to attach the objects to insert, because Data Context doesn’t need to know about the objects those are being added.

  • Call the relevant of one of those by looking at their status: InsertAllOnSubmit, DeleteAllOnSubmit

  • Do the same for the child entities of each. (Keep in mind that we need to delete all children regardless of their status if their parent is deleted)

So now hopefully the following implementation will be more understandable:

   1: /// <summary>
   2: /// Updates the users list.
   3: /// </summary>
   4: /// <param name=”updateList”>The list of users to perform the operations.</param>
   5: public void UpdateUsers(IList<User> updateList)
   6: {
   7:     using(FavoritesEntitiesDataContext context = new FavoritesEntitiesDataContext())
   8:     {
   9:         context.Users.AttachAll<User>(
  10: updateList.Where<User>(
  11:   usr=>usr.Status == EntityStatus.Updated ||
  12:   usr.Status == EntityStatus.Deleted), true);
  13:         context.Users.InsertAllOnSubmit<User>(
  14: updateList.Where<User>(
  15:   usr=>usr.Status == EntityStatus.New));
  16:         context.Users.DeleteAllOnSubmit<User>
  17: (updateList.Where<User>(usr => usr.Status == EntityStatus.Deleted));
  18: 
  19:         // do the same for the children
  20:         // If the parent is deleted, to prevent orphan records we need to delete
  21:         // children too
  22:         foreach (User user in updateList)
  23:         {
  24:             context.Favorites.AttachAll<Favorite>
  25: (user.Favorites.Where<Favorite>
  26:   (fav=>fav.Status == EntityStatus.Updated
  27:   || fav.Status == EntityStatus.Deleted
  28:   || fav.User.Status == EntityStatus.Deleted
  29:   || fav.User.Status == EntityStatus.Updated));
  30:             //we shouldnt insert the new child records of deleted entities
  31:             context.Favorites.InsertAllOnSubmit<Favorite>
  32: (user.Favorites.Where<Favorite>
  33:   (fav => fav.Status == EntityStatus.New
  34:   && fav.User.Status != EntityStatus.Deleted));
  35:             context.Favorites.DeleteAllOnSubmit<Favorite>
  36: (user.Favorites.Where<Favorite>
  37: (fav => fav.Status == EntityStatus.Deleted ||
  38: fav.User.Status == EntityStatus.Deleted));
  39:         }
  40: 
  41:         context.SubmitChanges();
  42:     }
  43: }

That’s the end of fun(!) folks. As you have seen already, there is some work involved with making Linq to SQL work in Multi Tiered architecture, but it is doable still. Again, download the sources and please don’t hesitate to post any comments, criticisms or crossword puzzles via here or sidarok@sidarok.com, they are all welcome.

kick it on DotNetKicks.com

Share it on: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogosphere News
  • e-mail
  • YahooMyWeb
  • DotNetKicks
  • DZone