|
|
These blog entries are written by industry experts and leaders. We consider this content to be a good read for any software developer or web technologist.
Browse by Tags
All Tags » LINQ » .NET » Data (RSS)
-
A few months ago we released an ASP.NET 3.5 Extensions Preview that contained a bunch of new features that will be shipping later this year (including ASP.NET AJAX Improvements, ASP.NET MVC, ASP.NET Silverlight Support, and ASP.NET Dynamic Data). The ASP.NET Dynamic Data support within that preview provided a first look at a cool new feature that enables you to quickly build data driven web-sites that work against a LINQ to SQL or LINQ to Entities object model. ASP.NET Dynamic Data allows you to automatically render fully functional data entry and reporting pages that are dynamically constructed from your ORM data model meta-data. In addition to supporting a dynamic rendering mode, it also allows you to optionally override and customize any of the view templates using any HTML or code you want (given you full control of the experience). ASP.NET Dynamic Data Preview Today we released an updated ASP.NET Dynamic Data Preview. You can learn more about it and download it here. This new dynamic data preview now works with the standard built-in ASP.NET data controls (GridView, ListView, FormView, DetailsView, etc). The dynamic data support enables these controls to automatically handle foreign-key relationships. For example, on a gridview you'll now get automatic friendly name display of foreign key column values and automatic drop-down list selection support of these values when in edit mode: The new dynamic data support also provides automatic UI validation support (both client-side and server-side) based on the constraints you set on your data model classes. For example, if a column in the database is limited to 50 characters in size, and is marked as non-nullable, appropriate UI control validators will automatically be applied by ASP.NET dynamic data to enforce this constraint in the UI pages as well. If you change the constraints within your LINQ to SQL or LINQ to Entities data model classes, the UI will automatically pick up these changes and enforce the new constraints on the next web request. In addition to standard data model metadata, you can also declare custom metadata to further control validation and the default display of UI of objects. You will be able to use all of the above features with both LINQ to SQL and LINQ to Entities. Visual Studio Dynamic Data Project Wizard In addition to the core ASP.NET dynamic data runtime support, the VS web tools team today also shipped a first preview of a new dynamic data project wizard that enables you to quickly get a data driven web-site started. The wizard allows you to select a database, and then the tables, views and sprocs within it that you want to build a LINQ to SQL data model around: After creating a data model, the wizard allows you to easily choose dynamic data driven template pages to build UI around it: You can then choose what type of inserting/editing/updating UI is supported on each page: And when you click finish it will setup a project with your data model classes and data UI pages setup to run. You can learn more about the wizard and watch it in action in a blog post and screencast here. How to Get Started You can learn more about this new dynamic data preview and download and run it locally here. You can watch David Ebbo's dynamic data presentation at MIX 08 to learn more about how it works. Also check out Scott Hunter's screen-cast here, and Brad Millington's screen cast here. You can ask questions and submit feedback via the www.asp.net forums here. Hope this helps, Scott 
|
-
The ASP.NET 3.5 Extensions CTP we shipped this past weekend contains a bunch of great new features. One of the cool new features is something we call "ASP.NET Dynamic Data Support". In a nutshell this enables you to really quickly build data driven web-sites that work against a LINQ to SQL (and in the future LINQ to Entities) object model - and optionally allows you to-do this without having to build any pages manually. The best way to see this in action is to watch David Ebbo's awesome 17 minute screen-cast: You can also follow the steps below to easily get started and use the dynamic data support: Step 1: Create a ASP.NET Dynamic Data Site: Once you have the ASP.NET 3.5 Extensions CTP release installed, you can use VS 2008 or the free Visual Web Developer 2008 Express to create a new Web Site or Web Application Project that has the ASP.NET dynamic data support enabled: This will create a new web-site you can use that has some default template files in them, along with CSS style-sheets you can use to customize the look and feel of them: Step 2: Add a LINQ to SQL Data Model to Your Site LINQ to SQL is an O/RM (object relational mapper) that ships in .NET 3.5, and which allows you to model a relational database using .NET classes. You can then query the database using LINQ, as well as update/insert/delete data from it. LINQ to SQL fully supports transactions, views, and stored procedures. It also provides an easy way to integrate data validation and business logic rules into your data model. The easiest way to get started with an ASP.NET Dynamic Data site is to add a new LINQ to SQL data model to your project (right-click->add new item->LINQ to SQL Classes item). This will bring up the LINQ to SQL class designer. Drag database tables from your server explorer to define a set of classes (with relationships) for your database schema: Step 3: Enable Dynamic Templates in Your Project: By default ASP.NET Dynamic Data projects provide built-in support for creating automatic "scaffolding" views of your LINQ to SQL and LINQ to Entities data models. Dynamic Data projects include template pages and user controls that can automatically create built-in data browsing/editing/selection/deleting UI that is dynamically constructed at runtime based on the LINQ to SQL or LINQ to Entities data model you've added to your project. To enable this auto-scaffolding support, open the web.config file of your project, search for the "dynamicData" section within it, and set the "enableTemplates" attribute to "true": Note: this auto-scaffolding feature is currently turned off by default for security reasons (we want you to be explicit about turning it on). Note: as you can see above, the ASP.NET dynamic data section provides optional URL mapping support that allows you to customize the URLs for your dynamic scaffold pages to be in whatever URL format you want. Step 4: Run Your Site Hit F5 to run your project. You'll by default be taken to a default.aspx page that lists all of the table objects in your data model: Note: the default.aspx home-page is in the project - if you don't like how it looks just open it up and edit it however you want. Clicking on any of the table names in the list above will take you to a dynamic data page that lists the data within it: The default data view pages automatically create functional data navigation UI that includes built-in data paging and editing support (and are ASP.NET AJAX enabled): The data view pages automatically infer relationships between your related objects and provide automatic filtering support: The data view pages also automatically show relationships between your objects and allow users to easily navigate amongst them. For example, notice how the Category column for products allows me to navigate to the category details for a specific product: The data view pages also enable automatic selection, editing and delete support. Notice below how when in inline edit mode they automatically infer friendly foreign key names (allowing you to edit the "Category" and pick a name from a dropdownlist instead of having to specify a "CategoryID" integer): All of this works without having to create a single custom .aspx page or write any custom code. All of the web UI is created dynamically off of your LINQ to SQL or LINQ to Entities data model. Step 5: Customizing the Site Further Having all of the above functionality work out of the box against your ORM data model is great for quickly getting your site up and running. Once you get the basic site experience working, you can optionally go in and fully customize it - both at the data validation/business logic level as well as at the UI layer. You can add standard LINQ to SQL data model and business validation rules to your data model in order to enforce constraints. The dynamic data pages will automatically work off of this logic, and provide appropriate error messages and validation UI based on them. You can also optionally go in and customize the UI of any of the data pages you want. Dynamic data projects allow you to customize the default look and feel of all data pages on the site, as well as customize the default look and feel of all column types used on the site. You can customize both of these via the default template files added under the "/App_Shared" folder when you create a new dynamic data project: Simply edit any of the files above to customize the default data page views and/or default data column appearances. You can also then optionally override the UI of specific data view pages by explicitly adding a .aspx page that maps to it in your project. For example, I could override the default ListDetails behavior for Products by adding a \Products\ListDetails.aspx page to my project: You can then add any standard ASP.NET data control to the page. You can also use the new dynamic data aware ASP.NET data controls that ship in the ASP.NET 3.5 Extensions CTP to the page. These allow you to completely customize the look and feel of the UI - while still using the new dynamic meta-data provider to infer relationships and meta-data about your data model classes for default behavior. Learning More The above walk-through only touches on a few of the scenarios and capabilities you can enable with ASP.NET Dynamic Data Projects. To learn more: - Watch David's webcast
- Check out the ASP.NET Dynamic Data QuickStarts
- Subscribe to David Ebbo's, Scott Hunter's, and Marcin Dobosz's blogs
- Visit the ASP.NET Dynamic Data Forum to ask questions
To learn more about LINQ to SQL and how you can use it to create data model classes, also check out my previous blog posts below: You can also watch the great LINQ and LINQ to SQL "How do I?" videos on the www.asp.net site here. Hope this helps, Scott 
|
-
Over the last few weeks I've been writing a series of blog posts that cover LINQ to SQL. LINQ to SQL is a built-in O/RM (object relational mapper) that ships in the .NET Framework 3.5 release, and which enables you to model relational databases using .NET classes. You can use LINQ expressions to query the database with them, as well as update/insert/delete data. Below are the first eight parts in this series: In Part 5 of the series I introduced the new <asp:LinqDataSource> control in .NET 3.5 and talked about how you can use it to easily bind ASP.NET UI controls to LINQ to SQL data models. I also demonstrated how to use it a little more in a follow-up post I did that discusses the new <asp:ListView> control (Part 1 - Building a Product Listing Page with Clean CSS UI). In both of these articles the queries I performed were relatively straight-forward (the where clause worked against a single table of data). In today's blog post I'll demonstrate how to use the full query expressiveness of LINQ with the LinqDataSource control, and show how you can use any LINQ to SQL query expression with it. Quick Recap: <asp:LinqDataSource> with a Declarative Where Statement In these two posts I demonstrated how you can use the built-in filter capabilities of the LinqDataSource control to declaratively express a filter statement on a LINQ to SQL data model. For example, assuming we had created a LINQ to SQL data model for the Northwind database (which I covered how to-do in Part 2 of this series), we could declare a <asp:LinqDataSource> control on the page with a declarative <where> filter that returns back only those products in a specific category (specified via a querystring "categoryid" value): We could then point a <asp:gridview> control at the datasource and enable paging, editing, and sorting on it: When we run the above page we'll then have a GridView with automatic sorting, paging, and editing support against our Product data model: Using declarative <where> parameters like above works well for many common scenarios. But what happens if you want the Product filtering to be richer or more complex? For example, what if we only wanted to display products made by suppliers based in a dynamic set of countries? Using the <asp:LinqDataSource> Selecting Event To handle custom query scenarios you can implement an event handler to handle the "Selecting" event on the <asp:LinqDataSource> control. Within this event handler you can write whatever code you want to retrieve a data model result. You could do this with a LINQ to SQL query expression, or call a Stored Procedure or use a Custom SQL Expression to retrieve the LINQ to SQL data model. Once you retrieve a sequence of data, all you need to-do is to assign it to the "Result" property on the LinqDataSourceSelectEventArgs object. The <asp:LinqDataSource> will then use this sequence as its data to work with. For example, below is a LINQ to SQL query expression that retrieves only products from suppliers based in a specific set of countries: VB: C#: Note: you do not need to write your query expression in-line within the event handler. A cleaner approach would be to encapsulate it within a helper method that you just call from the event handler. I show how to create one of these helper methods in the beginning of my Part 8 blog post (using a GetProductsByCategory helper method). Now when we run our page using the custom Selecting event handler, we'll only see those products whose suppliers are located in our array of countries: One of the really cool things to notice above is that paging and sorting still work with our GridView - even though we are using a custom Selecting event to retrieve the data. This paging and sorting logic happens in the database - which means we are only pulling back the 10 products from the database that we need to display for the current page index in the GridView (making it super efficient). You might ask yourself - how is it possible that we get efficient paging and sorting support even when using a custom selecting event? The reason is because LINQ uses a deferred execution model - which means that the query doesn't actually execute until you try and iterate over the results. One of the benefits of this deferred execution model is that it enables you to nicely compose queries out of other queries, and effectively "add-on" behavior to them. You can learn more about this in my LINQ to SQL Part 3 blog post. In our "Selecting" event handler above we are declaring a custom LINQ query we want to execute and are then assigning it to the "e.Result" property. We haven't actually executed it yet though (since we didn't try and iterate through the results or call ToArray() or ToList() on it). The LINQDataSource is therefore able to automatically append on a Skip() and Take() operator to the query, as well as apply an "orderby" expression to it -- all of these values being automatically calculated from the page index and sort preference of the GridView. Only then does the LINQDataSource execute the LINQ expression and retrieve the data. LINQ to SQL then takes care of making sure that the sort and page logic is handled in the database - and that only the 10 product rows required are returned from it. Notice below how we can also still use the GridView to edit and delete data, even when using a custom LinqDataSource "Selecting" event: This editing/deleting support will work as long as our Selecting event assigns a Result query whose result sequence is of regular entity objects (for example: a sequence of type Product, Supplier, Category, Order, etc). The LINQDataSource can then automatically handle cases where UI controls perform updates against them. To learn more about how updates work with LINQ to SQL, please read Part 4 of this series. Then read Part 5 of the series to see Updates in action with the LinqDataSource. Performing Custom Query Projections with the Selecting Event One of the powerful features of LINQ is its ability to custom "shape" or "project" data. You can do this in a LINQ to SQL expression to indicate that you want to retrieve only a subset of values from an entity, and/or to dynamically compute new values on the fly using custom expressions that you define. You can learn more about how these LINQ query projection/shaping capabilities in Part 3 of this series. For example, we could modify our "Selecting" event handler to populate a GridView to display a custom set of Product information. In this Grid we'll want to display the ProductID, Product Name, Product UnitPrice, the Number of Orders made for this Product, and the total Revenue collected from orders placed for the Product. We can dynamically compute these last two values using a LINQ expression like below: VB: C#: Note: The Sum method used in the Revenue statement above is an example of an Extension Method. The function it takes is an example of a Lambda expression. The resulting type created by the LINQ query expression is an anonymous type - since its shape is inferred from the query expression. Extension Methods, Lambda Expressions, and Anonymous Types are all new language features of VB and C# in VS 2008. The result of our custom LINQ expression when bound to the GridView will be UI like below: Note that paging and sorting still work above with our GridView - even though we are using a custom LINQ shape/projection for the data. One feature that will not work with custom shapes/projections, though, is inline editing support. This is because we are doing a custom projection in our Selecting event, and so the LinqDataSource has no way to safely know how to update an underlying entity object. If we want to add editing support to the GridView with a custom shaped type, we'd want to either move to using an ObjectDataSource control (where we could supply a custom Update method method to handle the updates), or have the user navigate to a new page when performing updates - and display a DetailsView or FormView control that was bound to a Product entity for editing (and not try and do inline editing with the grid). Summary You can easily perform common query operations against a LINQ to SQL data model using the built-in declarative filtering support of the LinqDataSource. To enable more advanced or custom filtering expressions, you can take advantage of the LINQDataSource's Selecting event. This will enable you to perform any logic you want to retrieve and filter LINQ to SQL data. You can call methods to retrieve this data, use LINQ Query Expressions, call a Stored Procedures, or invoke a Custom SQL Expression to-do this. Hope this helps, Scott 
|
-
Over the last few weeks I've been writing a series of blog posts that cover LINQ to SQL. LINQ to SQL is a built-in O/RM (object relational mapper) that ships in the .NET Framework 3.5 release, and which enables you to model relational databases using .NET classes. You can use LINQ expressions to query the database with them, as well as update/insert/delete data. Below are the first six parts in this series: In part 6 I demonstrated how you can optionally use database stored procedures (SPROCs) and user defined functions (UDFs) to query and retrieve data using your LINQ to SQL data model. In today's blog post I'm going to discuss how you can also optionally use SPROCs to update/insert/delete data from the database. To help illustrate this - let's start from scratch and build-up a data access layer for the Northwind sample database: Step 1: Creating our Data Access Layer (without using SPROCs yet) In my Part 2: Defining our Data Model Classes tutorial I discussed how to use the LINQ to SQL ORM designer that is built-in to VS 2008 to create a LINQ to SQL class model like below:
Adding Validation Rules to our Data Model Classes After defining our data model classes and relationships we'll want to add some business logic validation to our data model. We can do this by adding partial classes to our project that add validation rules to our data model classes (I cover how to-do this in-depth in my Part 4: Updating our Database LINQ to SQL tutorial). For example, we could add validation rules to enforce that the Customer's phone number follows a valid phone pattern, and that we don't add Orders where the customer's RequiredDate for delivery is before the actual OrderDate of the Order. Once defined in partial classes like below, these validation methods will automatically be enforced anytime we write code to update our data model objects in an application. VB:
C#:
Adding a GetCustomer() Helper Method to our DataContext Now that we have our data model classes created, and have applied validation rules to them, we can query and interact with the data. We can do this by writing LINQ expressions against our data model classes to query the database and populate them (I cover how to-do this in my Part 3: Querying our Database LINQ to SQL tutorial). Alternatively we could map SPROCs to our DataContext and use them to populate the data model classes (I cover how to-do this in my Part 6: Retrieving Data using Stored Procedures LINQ to SQL tutorial). When building a LINQ to SQL data layer you'll usually want to encapsulate common LINQ queries (or SPROC invocations) into helper methods that you add to your DataContext class. We can do this by adding a partial class to our project. For example, we could add a helper method called "GetCustomer()" that enables us to lookup and retrieve a Customer object from the database based on their CustomerID value: VB:
C#:
Step 2: Using our Data Access Layer (still without SPROCs) We now have a data access layer that encapsulates our data model, integrates business validation rules, and enables us to query, update, insert, and delete the data. Let's look at a simple scenario using it where we retrieve an existing customer object, update the customer's ContactName and Phone Number, and then create a new Order object and associate it with them. We can write the below code to do all of this within a single transaction. LINQ to SQL will ensure that our business logic validation rules are clean before saving anything in the database: VB:
C#:
LINQ to SQL monitors the modifications we make to the objects we retrieve from the DataContext, and keeps track of all of the objects we add into it. When we call DataContext.SubmitChanges() at the end, LINQ to SQL will check that our business logic rules are valid, and if so automatically generate the appropriate dynamic SQL to update our Customer record above, and insert a new record into the Orders table. Hang on a second - I thought this post was about using SPROCs??? If you are still reading this, you might be feeling confused about where SPROCs fit into this post. Why did I show you above how to write code that works with our data model objects, and then causes dynamic SQL to run? Why haven't I showed you how to call a SPROC for doing inserts/updates/deletes instead yet? The reason is that the programming model in LINQ to SQL for working with data model objects backed by SPROCs is the same as those updated via dynamic SQL. The way you add data model validation logic is exactly the same (so all the validation rules on our data model classes above still apply when we use SPROCs). The code snippet above where we use our data access layer to retrieve a customer, update it, and then add a new order associated with it is also exactly the same regardless of whether we are using dynamic SQL for updates, or whether we have configured our data model classes to use SPROCs instead. This programming model symmetry is powerful both in that you don't have to learn two ways of doing things, and also because it means that you don't have to decide up front at the beginning of your project whether you are going to use SPROCs or not. You can start off using the dynamic SQL support provided by the LINQ to SQL ORM for all queries, inserts, updates and deletes. You can then add your business and validation rules to your model. And then later you can optionally update your data mapping model to use SPROCs - or not if you decide you don't want to. The code and tests you write against your data model classes can stay the same regardless of whether you use dynamic SQL or SPROCs. We'll now spend the rest of this blog post demonstrating how we can update the data model we've built to use SPROCs for updates/inserts/deletes - while still using the same validation rules, and working with the same code snippets above. How to Use SPROCs for Insert/Update/Delete Scenarios We can modify the data access layer we've been building to use SPROCs to handle updates, instead of dynamic SQL, in one of two ways: 1) By using the LINQ to SQL designer to graphically configure SPROCs to execute in response to Insert/Update/Delete operations on our data model classes. or: 2) By adding a NorthwindDataContext partial class in our project, and then by implementing the appropriate Insert/Update/Delete partial methods provided on it (for example: InsertOrder, UpdateOrder, DeleteOrder) that will be called when we insert/update/delete data model objects. These partial methods will be passed the data model instances we want to update, and we can then execute whatever SPROC or SQL code we want to save it within the database. When we use approach #1 (the LINQ to SQL designer) to graphically configure SPROCs to call, it is under the covers generating the same code (in a partial class it creates) that you'd write when using approach #2. In general I'd recommend using the LINQ to SQL designer to configure the SPROCs for the 90% case - and then in more advanced scenarios go in and custom tweak the SPROC invocation code it generates if you need to. Step 3: Doing Order Inserts with a SPROC We'll begin switching our data model to use SPROCs by starting with the Order object. We'll first go to the Visual Studio "Server Explorer" window, expand into the "Stored Procedures" node of our database, and then right-click and choose "Add New Stored Procedure": We'll then create a new SPROC that we'll call "InsertOrder" that inserts a new order record into our Orders table: Notice above how the SPROC defines the "OrderID" parameter as an output param. This is because the OrderID column in the database is an identity column that is set to auto-increment each time a new record is added. The caller of the SPROC will pass in NULL as a value when calling it - and the SPROC then passes back the newly created OrderID value as the output value (by calling the SCOPE_IDENTITY() function at the end of the SPROC). After creating the SPROC we'll then open up the LINQ to SQL ORM designer for our data access layer. Like I discussed in my last blog post in this series (Part 6: Retrieving Data Using Stored Procedures), we can drag/drop SPROCs from the server-explorer onto the method pane of our DataContext designer. We'll want to-do this with our newly created InsertOrder SPROC: Our last step will be to tell our data access layer to use the InsertOrder SPROC when inserting new Order objects into the database. We can do that by selecting the "Order" class in the LINQ to SQL ORM designer, and then by going to the property grid and clicking the "..." button to override how Insert operations happen for it: Clicking the "..." button will bring up a dialog that allows us to customize how insert operations happen: Notice above how the default mode ("Use Runtime") is to have LINQ to SQL calculate and execute dynamic SQL to handle the insert operations. We can change that by selecting the "Customize" radio button and then pick our InsertOrder SPROC from the list of available SPROCs: The LINQ to SQL designer will populate a parameter list for the SPROC we picked, and enable us to map properties on our Order class to parameters of our InsertOrder SPROC. By default it is smart and tries to "best match" them based on name. You can go in and override them if you want. Once we click "ok" on the dialog, we are done. Now whenever a new Order is added to our DataContext and the SubmitChanges() method is invoked, our InsertOrder SPROC will be used instead of executing dynamic SQL. Important: Even though we are now using a SPROC for persistence, the custom Order "OnValidate()" partial method we created earlier (in step 1 of this blog post) to encapsulate Order validation rules still executes before any changes are saved or the SPROC is invoked. This means we have a clean way to encapsulate business and validation rules in our data models, and can re-use them regardless of whether dynamic SQL or SPROCs are used. Step 4: Doing Customer Updates with a SPROC Now let's modify our Customer object to handle updates using a SPROC. We'll start by creating a new "UpdateCustomer" SPROC like below: Notice above how in addition to passing in the @CustomerID parameter, we are also passing in a @Original_CustomerID parameter. The CustomerID column in the Customers table is not an auto-increment identity column, and it can be modified as part of an update of the Customer object. Consequently we need to be able to provide the SPROC with both the original CustomerID and the new CustomerID in order to update the record. We'll look at how we map this using the LINQ to SQL designer shortly. You'll notice above how I'm also passing in a @Version parameter (which is a timestamp) to the SPROC. This is a new column I've added to the Northwind Customers table to help handle optimistic concurrency. I will cover optimistic concurrency in much more depth in a later blog post in this LINQ to SQL series - but the short summary is that LINQ to SQL fully supports optimistic concurrency, and enables you to use either a version timestamp or to supply both original/new values to your SPROCs to detect if changes have been made by another user since you last refreshed your data objects. For this sample I'm using a timestamp since it makes the code much cleaner. Once we've created our SPROC, we can drag/drop it onto the LINQ to SQL designer to add it as a method on our DataContext. We can then select the Customer class in the ORM designer and click the "..." button to override the Customer object's Update behavior in the property grid: We'll select the "Customize" radio button and pick our UpdateCustomer SPROC to use: When mapping our Customer object's properties to the SPROC parameters, you'll notice that we'll want to be deliberate about whether we supply the "current" property value on the data object, or the original value that was in the database when the object was first retrieved. For example, we'll want to make sure we map the "current" value of the Customer.CustomerID property for the @CustomerID SPROC parameter, and that we map the original value for the @original_customerID SPROC parameter. Once we click "ok" on the dialog, we are done. Now whenever a new Customer is updated and the SubmitChanges() method is invoked, our UpdateCustomer SPROC will be used instead of executing dynamic SQL. Important: Even though we are now using a SPROC for persistence, the Customer "OnPhoneChanging()" partial method we created earlier (in step 1 of this blog post) to encapsulate Phone Number validation rules still executes before any changes are saved or the SPROC is invoked. We have a clean way to encapsulate business and validation rules in our data models, and can re-use them regardless of whether dynamic SQL or SPROCs are used. Step 5: Using our Data Access Layer Again (this time with SPROCs) Once we've updated our data layer to use SPROCs instead of dynamic SQL for persistence, we can re-run the exact same code we wrote in Step 2 earlier against our data model classes: Now the updates for the Customer object, and the insert for the Order object, are executing via SPROCs instead of dynamic SQL. The validation logic we defined still executes just like before, though, and the data access code we write to use the data model classes is exactly the same. Some Advanced Notes When Using SPROCs A few quick notes that you might find useful for more advanced SPROC scenarios with LINQ to SQL: Usage of SPROC Output Params: In the Insert scenario (Step 3) above I showed how we could return back the new OrderID value (which is an auto-increment identity column in the Orders table) using an output parameter of the SPROC. You aren't limited to only returning back identity column values when using SPROCs with LINQ to SQL - in fact you can update and return back output values for any parameter of your SPROC. You can use this approach both for Insert and Update scenarios. LINQ to SQL will then take the return value and use it to update the property value of your data model object without you having to-do any second queries against the database to refresh/populate them. What Happens if the SPROC Throws an Error? If a SPROC raises an error when doing an Insert/Update/Delete operation, LINQ to SQL will automatically cancel and rollback the transaction of all changes associated with the current SubmitChanges() call on the DataContext. This ensures that your data is always kept in a clean, consistent state. Can you write code instead of using the ORM designer to call SPROCs? As I mentioned earlier in this post, you can use either the LINQ to SQL ORM designer to map your insert/update/delete operations to SPROCs, or you can add partial methods on your DataContext class and programmatically invoke them yourself. Here is an example of the explicit code you could write in a partial class for the NorthwindDataContext to override the UpdateCustomer behavior to call a SPROC: The code above was actually what was generated by the LINQ to SQL ORM designer when we used the designer to map the SPROC and then associate the update behavior of the Customer object with it. You can use it as a starting point and then add any additional logic you want to it to make it more advanced (for example: use the return value of the SPROC to raise custom exceptions for error conditions, optimistic concurrency, etc). Summary LINQ to SQL is a very flexible ORM. It enables you to write clean object-oriented code to retrieve, update and insert data. Best of all - it enables you to cleanly design data model classes independent of how they are persisted and loaded from a database. You can use the built-in ORM engine to efficiently retrieve and update data in the database using dynamic SQL. Or alternatively you can configure your data layer to use SPROCs. The nice thing is that your code consuming the data layer, and all of the business logic validation rules you annotate it with, can be the same regardless of which persistence approach you use. In future blog posts in this series I'll cover some remaining LINQ to SQL concepts including: Single Table Inheritance, Deferred/Eager Loading, Optimistic Concurrency, and handling Multi-Tier scenarios. I'm on vacation next week, and so will hopefully have some free time to get a few of them written then. Hope this helps, Scott 
|
-
Over the last few weeks I've been writing a series of blog posts that cover LINQ to SQL. LINQ to SQL is a built-in O/RM (object relational mapper) that ships in the .NET Framework 3.5 release, and which enables you to model relational databases using .NET classes. You can use LINQ expressions to query the database with them, as well as update/insert/delete data. Below are the first five parts of my LINQ to SQL series: In these previous LINQ to SQL blog posts I demonstrated how you could use LINQ query expressions to programmatically retrieve data from a database. In today's blog post I'll cover how you can also use database stored procedures (SPROCs) and user defined functions (UDFs) with your LINQ to SQL data model. Today's blog post will specifically cover how to call SPROCs to query and retrieve data from the database. In my next blog post in this series I'll then show how you can optionally also use SPROCs to update/insert/delete data from the database. To SPROC or not to SPROC? That is the question.... The question of whether to use Dynamic SQL generated by an ORM or instead use Stored Procedures when building a data layer is a topic that generates endless (very passionate) debate amongst developers, architects and DBAs. A lot of people much smarter than me have written on this topic, so I won't rehash the arguments for and against each side here. The LINQ to SQL ORM that ships in .NET 3.5 is pretty flexible, and can be used to create data model classes whose object model can be independent of the underlying database schema, and which can encapsulate business logic and validation rules that work regardless of whether the data model is populated/persisted via dynamic SQL or via SPROCs. In my LINQ to SQL Part 3: Querying our Database post I discussed how you can write LINQ query expressions against a LINQ to SQL data model using code like below:
When you write LINQ query expressions like this the LINQ to SQL ORM will execute the necessary dynamic SQL for you to retrieve Product objects that matches your query. As you'll learn in this post, you can also optionally map SPROCs in the database to your LINQ to SQL DataContext class, which allows you to alternatively retrieve the same Product objects by calling a stored procedure instead:
This ability to use both dynamic SQL and SPROCs with a clean data model layer is pretty powerful, and provides a great deal of flexibility when working on projects. The Steps to Map and Call a SPROC using LINQ to SQL In my Part 2: Defining our Data Model Classes tutorial I discussed how to use the LINQ to SQL ORM designer to create a LINQ to SQL class model like below:
Notice above how there are two panes on the LINQ to SQL ORM designer surface. The left pane enables us to define data model classes that map to our database. The right method pane allows us to optionally map SPROCs (and UDFs) to our LINQ to SQL DataContext object, which we can then use in-place of dynamic SQL to populate the data model objects. How to Map a SPROC to a LINQ to SQL DataContext To map SPROCs to our DataContext class, let's first go to the VS 2008 Server Explorer window and look at the SPROCs within our database:
We can double click any of the SPROCs above to open and edit them. For example, below is the "CustOrderHist" SPROC in Northwind:
To map the above SPROC to our LINQ to SQL DataContext, we can drag/drop it from the Server Explorer onto our LINQ to SQL ORM designer. This will automatically create a new method on our LINQ to SQL DataContext class like below:
By default the method name created on the DataContext class will be the same as the SPROC name, and the return type of the method will be an automatically created type that follows the "[SprocName]Result" naming pattern. For example: the SPROC above would return a sequence of "CustOrderHistResult" objects. We could optionally change the name of the method by selecting it in the designer and then use the property grid to rename it. How to Call our Newly Mapped SPROC Once we've done the steps above to map a SPROC onto our DataContext class, it is easy to use it to programmatically retrieve data. All we need to-do is call the new method we mapped on our DataContext class to get back a sequence of strongly typed results from the SPROC: Calling the SPROC in VB:
Calling the Sproc in C#:
In addition to programming looping over the result like in the code samples above, I could also obviously bind the results to any UI control to display them. For example, the below code databinds the result of our SPROC to a <asp:gridview> control:
Which then displays the product history of our customer on a page like so:
Mapping the Return Type of SPROC Methods to Data Model Classes In the "CustOrderHist" SPROC example above the stored procedure returned a sequence of product history results containing two columns of data: the ProductName of the product, and the Total Number of orders the customer has made for that product. The LINQ to SQL designer automatically defined a new "CustOrderHistResult" class to represent this result. We could alternatively choose to have the return result of a SPROC map to an existing data model class we have already defined in the LINQ to SQL designer (for example: an existing Product or Order entity class). For example, assume we have a "GetProductsByCategory" SPROC in our database that returns product information like so:
Like before we can create a "GetProductsByCategory" method on our DataContext that calls this SPROC by dragging it onto our LINQ to SQL designer. Rather than just dropping the SPROC anywhere on the designer, though, we'll instead drop the SPROC on top of the existing "Product" class in our data model designer:
This gesture of dropping the SPROC onto the Product class tells the LINQ to SQL designer to have the "GetProductsByCategory" method return a sequence of "Product" objects as a return result:
One of the cool things about having our SPROC return "Product" objects like above is that LINQ to SQL will automatically track the changes made to the returned Product objects just like it would Product objects returned via LINQ queries. When we call the "SubmitChanges()" method on our DataContext, the changes we have made to these objects will automatically be saved back to the database. For example, we could write the code below to retrieve (using a SPROC) and change the price of all products within a specific Category to be 90% of their current value:
When we call SubmitChanges() at the end it will transactionally update all of the product prices. To understand more about how change tracking and the SubmitChanges() method work, as well as about how Validation Business Logic can be added to data model entities, please read my LINQ to SQL Part 4: Updating our Database tutorial. In my next blog post in this LINQ to SQL series I'll also cover how you can replace the dynamic insert/update/delete SQL generated by the ORM with custom SPROCs that handle the database updates instead. The nice thing is that the code above wouldn't change at all if I configured my DataContext to use SPROCs for updates - it would purely be a mapping layer change and the code written against my data model would be oblivious to it. Handling SPROC Output Parameters LINQ to SQL maps "out" parameters in SPROCs as reference parameters (ref keyword), and for value types declares the parameter as nullable. For example, consider the below "GetCustomerDetails" SPROC which takes a CustomerID as an input parameter, and which returns the company name as an output parameter in addition to its order history as a query result:
If we drag the above SPROC onto our "Order" class in the LINQ to SQL designer, we could then write the below code to call it: VB:
C#:
Notice in the code above how the SPROC helper method returns back a sequence of Order objects - but also then returns the CompanyName as an output parameter to the helper method. Handling Multiple Result Shapes from SPROCs When a stored procedure can return multiple result shapes, the return type of the SPROC method on the DataContext cannot be strongly typed to a single class shape. For example, consider the SPROC below which returns either a product result or an order result depending on the input parameter:
LINQ to SQL supports the ability to create SPROC helper methods that can return either a Product or Order shape by adding a partial "NorthwindDataContext" class to the project that defines a method (which in this case we'll call "VariablesShapeSample") that invokes the SPROC and returns an IMultipleResult object like so: VB:
C#:
Once this method is added into our project we can then call it and convert the result to be either a Product or Order sequence when we are using it: VB:
C#:
Supporting User Defined Functions (UDFs) In addition to SPROCS, LINQ to SQL also supports both scalar-valued and table-valued user defined functions (UDFs), as well as the in-line counterpart to both. Once added to your DataContext as a method, you can use these UDF functions within your LINQ queries. For example, consider a simple scalar user defined function called "MyUpperFunction":
We can drag/drop it from the Visual Studio Server Explorer onto our LINQ to SQL Designer to add it as a method on our DataContext:
We can then use this UDF function inline within our LINQ expressions when writing queries against our LINQ to SQL data model (notice it is being used within the "where" clause below): VB:
C#:
If you use the LINQ to SQL Debug Visualizer that I blogged about here, you can see how LINQ to SQL transforms the above expression queries into raw SQL that execute the UDF inside the database at runtime:
Summary LINQ to SQL supports the ability to call Stored Procedures and UDFs within the database and nicely integrate them into our data model. In this blog post I demonstrated how you can use SPROCs to easily retrieve data and populate our data model classes. In my next blog post in this series I'll cover how you can also use SPROCs to override the update/insert/delete logic when you SubmitChanges() on your DataContext to persist back to the database. Hope this helps, Scott 
|
-
One of the new controls in ASP.NET 3.5 that I think will be very popular is the <asp:ListView> control. The ListView control supports the data editing, insertion, deleting, paging and sorting semantics of higher-level controls like the GridView. But - unlike the GridView - it provides you with complete control over the html markup generated. The ListView control, when combined with the new Web Designer and rich CSS support in VS 2008, enables you to build much cleaner HTML UI. Over the next few weeks I'll be putting together several blog posts that show off how you can use it. Building a Products Catalog Page For today's blog post we are going to start simple and just build a basic product catalog listing page like below: This products.aspx page will take a Catalog index in the URL, and use LINQ to SQL to retrieve and display product information. We will also enable paging navigation at the bottom of the product listing (and do all of the paging operations in the database - so that only 6 products at a time are retrieved from the database). The HTML markup output from the server will be 100% CSS based (no tables or inline styles). Step 1: Defining out Site Layout with Nested Master Pages Before we start working on our product page, we'll first want to define the overall UI and layout structure for our site. If you are "design challenged" like me, one approach you might want to take with a new site is to get started by using one of the free HTML site templates that you can download from these two sites: http://www.opensourcetemplates.org/ or http://www.oswd.org/. The templates on these sites are pure HTML (meaning you can use them with any server-side programming technology), and are built using clean CSS and XHTML markup. For this blog post I decided to go with the "TerraFirma" template here. After I downloaded the template, the first thing I did was to create a root "Site.Master" Master Page that defined the overall layout structure for the site. I then created a few nested master pages to define different column style layouts ("SingleColumn.master" and "TwoColumn.master"). VS 2008 now has great support for creating and using nested master pages that makes doing this easy. You can read about how to define and use them in my earlier VS 2008 Nested Master Page Support blog post. Once we have created our master pages layouts, we can then create a new product catalog page for the site that is based on the single-column nested master page (click below for a full screen picture): Notice above how we can edit the page using the new VS 2008 Split View mode feature. Above I'm using the "Vertical Split View" option so that I can see both the source and design view on a wide-screen monitor. You can learn how to enable this in my earlier Enabling Vertical Split View in VS 2008 blog post. Step 2: Defining our CSS Rules using Mock HTML UI When it comes to defining our product UI for the page, there are a couple of different approaches we can use. One would be to start by writing code to generate some dynamic data UI, and then work to make it pretty. Another approach we could take would be to start by mocking up the HTML UI first, and then once we are happy with it write the code to make it dynamic. For this blog post I'm going to take this second approach. To start let's just add a standard <ul><li> list of product content into the page: As you can see above, this <ul> list looks pretty unattractive, and obviously not like what we want our product listing page to look like. To make it more attractive, we'll use some of the new CSS editing features I covered in my earlier VS 2008 Web Designer and CSS Support blog post. Specifically we'll want to open the new "Manage Styles" tool window in VS 2008 (you can open this by selecting the Format->CSS Styles->Managed Styles menu item): The manage styles window provides an easy way for us to see all CSS rules currently in our CSS stylesheet. It also enables us to quickly lookup CSS selector values, refactor css rules across stylesheets, and create new rules. We can create a new CSS rule for our product listing by selecting the "New Style..." link in the "Manage Styles" window. This will bring up a dialog that enables us to choose where we want to define the CSS rule, and configure what settings we want for it. For this sample we'll name the CSS selector ".productslist li" and select the "Define in existing style sheet" option to add it to the external stylesheet we already have for our application: We can then hit "ok", and return back to source mode to assign the CSS rule on our <ul> list (note how VS 2008 now provides CSS intellisense in source view): Currently our CSS rule doesn't have settings assigned to it, so our <ul> list will still look the same as it did before. We can change that by assigning some CSS settings. There are a couple of ways we could set these CSS settings: 1) open up the CSS stylesheet and set them in source mode, 2) use the manage styles dialog we saw before to set them, or 3) use the new CSS Properties Window to edit the CSS rules in real-time within the designer. We can bring up the CSS Properties Windows via the View->CSS Properties menu: When you select an element either in source-view or design-view, the CSS Properties Windows will list all of the CSS rules that apply to it. The "Applied Rules" list at the top of the CSS Properties window indicates the precedence order of cascading rules. The CSS properties list below it then shows all of the setting values assigned to that element. The "target rule" drop down in the style application toolbar (circled in red above) indicates which CSS selector a change in the CSS Properties window will be assigned to. In the example above our .productlist li rule is the CSS selector we currently have selected - which means as we set values in the CSS Properties window they will be persisted under that rule name in our external CSS stylesheet. No style settings will be persisted inline the HTML page. Let's now make some changes to our "productlist li" CSS rule. First we'll change the layout display mode to be "inline": We'll then want to float each <li> to the left: And lastly we'll set the bottom and left margin of each <li> element to be 15px to space out the products nicely: Notice how when we are done no inline styles have been saved in our HTML page: Instead they've all been cleanly saved under our "productlist li" CSS selector in the external CSS stylesheet: Now all that remains is for us to replace the static HTML with some dynamic data coming from a database. Step 3: Defining our LINQ to SQL Data Model We'll use LINQ to SQL to retrieve our product data from the database. LINQ to SQL is a great new ORM (object relational mapper) implementation built into .NET 3.5. You can learn more about it from my on-going LINQ to SQL blog series (more posts in it coming soon): We'll use the Northwind sample database for our product data, and define our data model classes in the LINQ to SQL ORM designer like so: Once we have our LINQ to SQL data model defined, we are ready to use the <asp:listview> control and bind the data to it. Step 4: Convert our HTML Mock UI to use the <asp:ListView> control The <asp:listview> control is a template-driven control. The control itself outputs no "built-in" UI, nor any actual HTML markup. Instead, you can define whatever markup *you* want displayed using the below templates: - LayoutTemplate
- ItemTemplate
- AlternatingItemTemplate
- SelectedItemTemplate
- EditItemTemplate
- InsertItemTemplate
- EmptyItemTemplate
- EmptyDataTemplate
- ItemSeparatorTemplate
- GroupTemplate
- GroupSeparatorTemplate
The first two templates in the list above - LayoutTemplate and ItemTemplate - are the most common ones you'll end up using. The <LayoutTemplate> template allows you to define the outer container/wrapper of your data UI. The <ItemTemplate> template then allows you to define what each item in the list should look like. Within the <LayoutTemplate> you then define an "ItemContainer" control that indicates where you want the <asp:ListView> control to dynamically add the <ItemTemplate> items into the output markup. To see how this works in action, we could take our mock products HTML UI: And replace it with a <asp:listview> that can dynamically generate the exact same markup output like so: Notice above how I am using a <asp:placeholder> control in the <LayoutTemplate> to indicate where I want to add in my items in the list. I could use other controls instead as the itemContainer if I wanted to - but by using an <asp:placeholder> control as the itemContainer I will prevent any id values or extra markup being generated. Notice above how I've also defined an <EmptyDataTemplate>. This will display instead of the <LayoutTemplate> if I assign an empty sequence of data to the ListView. This will avoid us inadvertently displaying an empty <ul></ul> element in the event that there are no products in the catalog specified. Once we've defined our template above, we can write some code in our code-behind file to retrieve our product data using LINQ to SQL, and bind our ListView with it: VB: C#: And now when we run the page and supply a valid categoryid as a querystring argument we'll see our products dynamically pulled from the database: If we try a category that doesn't contain products, we'll get our empty template message: If you do a "view source" on the products page in the browser, you'll see that the markup generated from our ListView control is the same as what our static HTML was: There are no ID elements or inline styles generated. We had complete control over every HTML element and attribute generated. Step 5: Using a <asp:LinqDataSource> control instead of writing code In our previous step above we wrote procedural LINQ to SQL code to databind our <asp:ListView>. This obviously works, and provides a great deal of control over the logic executed. Another option you can alternatively use is a declarative ASP.NET datasource control. All of the ASP.NET 2.0 datasource controls (SqlDataSource, ObjectDataSource, AccessDataSource, XmlDataSource, SiteMapDataSource, etc) work with the ListView. You can also use the new <asp:LinqDataSource> control with it. For more information on the LinqDataSource, check out my previous LINQ to SQL Part 5: Binding UI using the ASP:LinqDataSource Control blog post. To use the <asp:LinqDataSource> in the above sample, we'll first delete the previous code we wrote in the code-behind file, and then click on the <asp:ListView> control in the designer and select the "Choose Data Source->New DataSource" option. We'll pick the "LINQ DataSource" option in the data source dialog, and then bind to the Northwind data model we created earlier. We can then choose to bind the ListView against the "Products" entity collection in our Northwind data model: We can then hit the "Where" button to configure a LINQ filter based on the "category" value in the querystring (we could alternatively bind the value from a form value, cookie, session value, another control, etc): When we press the "ok" button the ListView's DataSourceID will be set to a new <asp:LinqDataSource> in the page: And now without us having to have any code in the application we have a product listing with custom HTML UI databinding against our LINQ to SQL data model. Step 6: Enabling Server Side Paging using the <asp:DataPager> control Our last step with this sample will be to enable paging support over the products data. Specifically, we only want to display 6 products at a time on the page, and provide a nice page number UI to allow users to navigate forward and backwards over our product sequence. One of the other new controls in ASP.NET 3.5 is the <asp:DataPager> control - which makes data paging scenarios with the ListView control pretty easy. You can drop it anywhere you want on a page, and set its "PagedControlID" property to point at the ListView, and its "PageSize" property to indicate how many items in the ListView you want displayed at a time: The <asp:DataPager> will then output navigation UI for your ListView: And then if you click the "2" link in the paging UI above it will show you the remaining 5 products in the category: The <asp:LinqDataSource> automatically uses LINQ to SQL's built-in support for server-side data paging to ensure that only 6 products (because the PageSize is 6) are ever retrieved from the database at a time. This means that your application and database will scale even if you have thousands of products in a specific category. Disclaimer: The <asp:DataPager> in Beta2, though, does have some limitations in that it can't by default be driven off of a querystring value - which makes it not very SEO friendly. I'll cover how to fix this and support a querystring index in a future blog post. Summary Hopefully the above walkthrough provides a good first look overview of how to use the new <asp:ListView> control. You will find that this control provides you with complete control over the markup output sent down to a client - while still providing rich data paging, editing, deleting and insertion semantics. I'll cover many more of these scenarios (along with the ListView's cool grouping functionality) in future blog posts. Click here to download a copy of the above sample in order to try it out on your own machine. Hope this helps - and have a great weekend! Scott 
|
-
One of the big programming model improvements being made in .NET 3.5 is the work being done to make querying data a first class programming concept. We call this overall querying programming model "LINQ", which stands for .NET Language Integrated Query. LINQ supports a rich extensibility model that facilitates the creation of efficient domain-specific providers for data sources. .NET 3.5 ships with built-in libraries that enable LINQ support against Objects, XML, and Databases. What is LINQ to XML? LINQ to XML is a built-in LINQ data provider that is implemented within the "System.Xml.Linq" namespace in .NET 3.5. LINQ to XML provides a clean programming model that enables you to read, construct and write XML data. You can use LINQ to XML to perform LINQ queries over XML that you retrieve from the file-system, from a remote HTTP URL or web-service, or from any in-memory XML content. LINQ to XML provides much richer (and easier) querying and data shaping support than the low-level XmlReader/XmlWriter API in .NET today. It also ends up being much more efficient (and uses much less memory) than the DOM API that XmlDocument provides. Using LINQ to XML to query a local XML File To get a sense of how LINQ to XML works, we can create a simple XML file on our local file-system like below that uses a custom schema we've defined to store RSS feeds: I could then use the new XDocument class within the System.Xml.Linq namespace to open and query the XML document above. Specifically, I want to filter the <Feed> elements in the XML file and return a sequence of the non-disabled RSS feeds (where a disabled feed is a <Feed> element with a "status" attribute whose value is "disabled"). I could accomplish this by writing the code below: VB: C#: Notice in the code-snippets above how I'm loading the XML file using the XDocument.Load(path) static method - which returns back an XDocument object. Because I'm running this code within ASP.NET, I'm using the Server.MapPath(path) helper method to resolve the correct path for my XML file relative to the page I'm running the code on. Once I have an XDocument object for my XML file I can then write a LINQ query expression to retrieve the XML data I'm looking for. In the code above I'm querying over each of the <Feed> elements within the XML file. This is driven by this opening clause in the LINQ query expression: from feed in feedXML.Decedents("Feed") I'm then applying a filter that only returns back those "Feed" elements that either don't have a "status" attribute, or whose "status" attribute value is not set to "disabled": Where (feed.Attribute("status") Is Nothing) OrElse (feed.Attribute("status").Value <> "disabled") I am then using the select clause in our LINQ expression to indicate what data I want returned. If I simply wrote "select feed", LINQ to XML would return back a sequence of XElement objects that represents each of the XML element nodes that match my filter. In the code samples above, though, I am using the shaping/projection features of LINQ to instead define a new anonymous type on the fly, and I am defining two properties on it - Name and Feed - that I want populated using the <Name> and <Url> sub-elements under each <Feed> element: Select Name = feed.Element("Name").Value, Url = feed.Element("Url").Value As you can see above (and below), I can then work against this returned sequence of data just like I would any collection or array in .NET. VS 2008 provides full intellisense and compilation checking support over this anonymous type sequence: I can also data-bind the results against any UI control in ASP.NET, Windows Forms, or WPF. For example, assuming I had a dropdownlist control defined in my page like so: I could use the below LINQ to XML code to databind the results to it: This will then produce a nice drop-downlist in our HTML page like so: Hmm - What is this "anonymous type" thing? In my code above I've taken advantage of a new language feature in VB and C# called "anonymous types". Anonymous types enable developers to concisely define inline CLR types within code, without having to explictly define a formal class declaration of the type. You can learn more about them in my previous New "Orcas" Language Feature: Anonymous Types blog post. While anonymous types can be super useful when you want to locally iterate and work with data, we'll often want/need to define a standard class when passing the results of our LINQ query between multiple classes, across class library assemblies, and over web-services. To enable this, I could define a non-anonymous class called "FeedDefinition" to represent our Feed data like so: Note above how I'm using the new "Automatic Properties" feature of C# to define the properties (and avoid having to define a field for them). I could then write the below method to return back a generics based List<FeedDefinition> collection containing FeedDefinition objects: Note above how the only change I've made to the LINQ to XML query we were using before is to change the "select" clause from "select new" (with no type-name) to "select new FeedDefinition". With this change I'm now returning a sequence of FeedDefinition objects that I can pass from class to class, assembly to assembly, and across web-services. Using LINQ to XML to Retrieve a Remote RSS XML Feed The XDocument.Load(path) static method supports the ability open both XML files from the file-system, as well as remote XML feeds returned from an HTTP URL. This enables you to use it to access remote RSS feeds, REST APIs, as well as any other XML feed published on the web. For an example of this in action, let's take a look at the XML of my blog's RSS feed (http://weblogs.asp.net/scottgu/rss.aspx): I could write the LINQ to XML code below to retrieve the above blog post data from my RSS feed, and work with the individual feed items as .NET objects: Note above how I am converting the "Published" field in the RSS field - which is a string in the XML - to a .NET DateTime object. Notice also how LINQ to XML includes a built-in XNamespace type that provides a type-safe way to declare and work with XML Namespaces (which I need to-do to retrieve the <slash:comments> element). I could then take advantage of the composition features of LINQ to perform a further sub-query on the result, so that I filter over only those RSS posts that were published within the last 7 days using the code below: As you can see above, you can feed the results of one LINQ query expression to be the input of another LINQ expression. This enables you to write very clean, highly composable, code. Using LINQ Sub-Queries within a LINQ to XML Query Expression If you look at the raw XML of my RSS feed, you'll notice that the "tag" comments for each post are stored as repeated <category> elements directly below each <item> element: When designing the object model for a "BlogEntry" class, I might want to represent these <category> values as a sub-collection of strings. For example, using a "Tags" property that is a generic list of type string: You might be wondering - how do we take a flat collection of <category> elements under <item> and transform them into a nested sub-collection of strings? The nice thing about LINQ is that it makes this type of scenario easy by allowing us to use nested LINQ query expressions like so: This "shaping" power of LINQ, and its ability to take flat data structures and make them hierarchical (and take hierarchical data structures and make them flat) is super powerful. You can use this feature with any type of data source - regardless of whether it is XML, SQL, or plain old objects/arrays/collections. Putting it all Together with a Simple RSS Feed Reader The code snippets I've walked through above demonstrate how you can easily write LINQ to XML code to retrieve a list of RSS feeds from a local XML file, and how to remotely query an RSS feed to retrieve an individual feed's details and individual item post contents. I could obviously then take the resulting feed contents and data-bind it to a ASP.NET GridView or ListView control to provide a nice view of the blog feed: I've built a simple sample application that puts all of these snippets together to deliver a simple RSS Reader with LINQ to XML and the new <asp:ListView> control. You can download it here. Included in the download is both a VB and C# version of the application. Summary LINQ to XML provides a really powerful way to efficiently query, filter, and shape/transform XML data. You can use it both against local XML content, as well as remote XML feeds. You can use it to easily transform XML data into .NET objects and collections that you can further manipulate and transfer across your application. LINQ to XML uses the same core LINQ query syntax and concepts that LINQ to SQL, LINQ to Objects, LINQ to SharePoint, LINQ to Amazon, LINQ to NHibernate, etc. use when querying data. You can learn more about the LINQ query syntax and the supporting language features being added to VB and C# to support it from these previous blog posts of mine: You might also find these blog posts of mine useful to learn more about LINQ to SQL: In a future blog post I'll return to LINQ to XML and demonstrate how it can be used not just to query XML, but also to really cleanly generate XML output from a .NET data structure. Hope this helps, Scott 
|
-
Probably the biggest programming model improvement being made in .NET 3.5 is the work being done to make querying data a first class programming concept. We call this overall querying programming model "LINQ", which stands for .NET Language Integrated Query. Developers can use LINQ with any data source, and built-in libraries are included with .NET 3.5 that enable LINQ support against Objects, XML, and Databases. Earlier this summer I started writing a multi-part blog series that discusses the built-in LINQ to SQL provider in .NET 3.5. LINQ to SQL is an ORM (object relational mapping) implementation that allows you to model a relational database using .NET classes. You can then query the database using LINQ, as well as update/insert/delete data from it. LINQ to SQL fully supports transactions, views, and stored procedures. It also provides an easy way to integrate data validation and business logic rules into your data model. You can learn more about LINQ to SQL by reading my posts below (more will be coming soon): Using the LINQ to SQL Debug Visualizer One of the nice development features that LINQ to SQL supports is the ability to use a "debug visualizer" to hover over a LINQ expression while in the VS 2008 debugger and inspect the raw SQL that the ORM will ultimately execute at runtime when evaluating the LINQ query expression. For example, assume we write the below LINQ query expression code against a set of data model classes: We could then use the VS 2008 debugger to hover over the "products" variable after the query expression has been assigned: And if we click the small magnifying glass in the expression above, we can launch the LINQ to SQL debug visualizer to inspect the raw SQL that the ORM will execute based on that LINQ query: If you click the "Execute" button, you can even test out the SQL query and see the raw returned results that will be returned from the database: This obviously makes it super easy to see precisely what SQL query logic LINQ to SQL ORM is doing for you. You can learn even more about how all this works by reading the | |
|