Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 802 Part V Data Connectivity LINQ to Entities is more than a simple ORM tool. The ADO.NET Entity Framework and LINQ to Enti- ties enable developers to work against a conceptual model that offers more flexible mapping, which pro- vides the capability to utilize a wider degree of variance from the underlying data source. What determines whether a project should use LINQ to SQL or LINQ to Entities/Entity Framework? You should use LINQ to SQL when you want the following: ■ A rapid development cycle ■ An ORM solution and a 1:1 data/object model mapping ■ Optimized performance through stored procedures and compiled queries ■ To generate your own CLR classes versus using generated classes or deriving from base classes In contrast, LINQ to Entities and the Entity Framework should be used when the following apply: ■ Your application targets different database engines in addition to Microsoft SQL Server ■ Your physical database structure could be significantly different from your object model. That means you still want the benefits of an ORM but your classes may or may not map 1:1 with your database schema. ■ You want to optimize performance through stored procedures and compiled queries ■ The LINQ query should work in a database vendor–neutral manner ■ You want to define application domain models to be used as the basis for a persistence layer This section introduces LINQ to Entities and the Entity Framework and illustrates by example how easy it is to create entities and query them using LINQ. Everything needed to use LINQ to Entities and the Entity Framework is installed with Visual Studio 2008 SP1, which can be downloaded from the following location: www.microsoft.com/downloads/details.aspx?FamilyId=FBEE1648-7106-44A7- 9649-6D9F6D58056E&displaylang=en Creating and querying entities using LINQ After installing the service pack, create a new Visual Studio C# Windows forms project. When the project is loaded, add a new item to it. In the Add New Item dialog you will notice a new item in the list of templates called ADO.NET Entity Data Model. This Entity Data Model provides the capability to define domain models for an application. Select the ADO.NET Entity Data Model template, name it AdventureWorks.edmx, and then click OK. The Entity Data Model wizard begins. The first screen of the wizard provides the capability to define the model contents. The model can be created from scratch, by selecting Empty Model, or generated from a database, by selecting Generate from Database. By default, Generate from Database is selected, so ensure that this option is selected and click Next. The next step of the wizard defines the data connection. If a connection has previously been defined, you can select it from the drop-down menu. If not, click the New Connection button and define a 802 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 803 LINQ 34 connection in the Connection Properties dialog. You are also asked at this step whether or not you want to store sensitive connection information in the connection string. The connection string information is stored in the application configuration file ( app.config) and is used by the ObjectContext to connect to the database. Excluding the sensitive data (such as password information) from the connection string requires that the information be set in the application code. Including the sensitive data includes it in clear text in the app.config. For production environments, it is recommended that sensitive information be excluded from the connection string. For the purposes of this example, select the option to include the sensitive information in the connection string. Once the connection has been defined, click Next. The next step of the wizard enables you to select the objects to be included in the model. Objects such as tables, views, and stored procedures can be selected for inclusion. For this example, select the Person.Contact, HumanResources.Employee, Sales.Customer,and Sales.SalesOrderHeader tables. In addition, it is at this step of the wizard that the model namespace must be entered. It defaults to a value, but it is good to verify that there is a value. By default, it takes the object name entered when the ADO.NET Entity Data Model template was selected, which in this example is AdventureWorks, and then adds the word ‘‘Model’’ to the end. Thus, the namespace should default to AdventureWorksModel in this example. Click Finish. The wizard then builds the Entity Data Model based on the objects and selections in the wizard. When it is complete, the Entity Data Model Designer opens in the Visual Studio IDE. This process is very familiar to the LINQ to SQL example shown earlier. The difference is that with LINQ to SQL, the database objects were added in the model after the designer was created. With the Entity Framework, a wizard walks the developer through the steps of selecting which objects to include in the model prior to creating the designer and model. When the creation of the model is complete, a new node appears in the Solution Explorer. The node is called AdventureWorks.edmx and it has a child file, which is the Designer file called AdventureWorks.Designer.cs. Opening the . edmx file opens the visual model designer that was created at the end of the wizard. Our focus here is the Designer.cs file. Opening that file displays some code that should look very familiar. The Designer.cs file contains the DataContext and all the necessary object mappings to the database. For example, one of the first lines that should look familiar is the creation of a class that inherits from the DataContext: public partial class AdventureWorksEntities : global::System.Data.Objects.ObjectContext Also different is the way tables are defined. Instead of applying the [Table] attribute, as done in LINQ to SQL, this class inherits from the EntityObject class, which is the base class for entity types that are generated via the Entity Data Model tools, as shown here: public partial class Employee : global::System.Data.Objects.DataClasses.EntityObject 803 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 804 Part V Data Connectivity The class is also attributed with several attributes, such as AdmEntityTypeAttribute, which indi- cates that the class represents an entity type: [global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute (NamespaceName="AdventureWorksModel", Name="Employee")] [global::System.Runtime.Serialization.DataContractAttribute (IsReference=true)] [global::System.Serializable()] public partial class Employee : global::System.Data.Objects.DataClasses.EntityObject Also different is the way in which columns are defined and mapped. Instead of using the [Column] attribute, as done in LINQ to SQL, columns are attributed with the EdmScalarProperty Attribute , which indicates that the property represents a scalar property: [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute (EntityKeyProperty=true, IsNullable=false)] [global::System.Runtime.Serialization.DataMemberAttribute()] public int EmployeeID Another important item to look at is the set of relationship attributes. The EdmRelationship Attribute is used to define a relationship between two entity types (in this case, Contacts and Employees) based on an association in the conception model: [assembly: global::System.Data.Objects.DataClasses .EdmRelationshipAttribute("AdventureWorksModel", "FK_Employee_Contact_ContactID", "Contact", global::System.Data.Metadata.Edm.RelationshipMultiplicity.One, typeof(EF.Contact), "Employee", global::System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(EF.Employee))] With that background, it is time to focus on the front end. Open the form in design view and place a list box and a button on the form. In the Click event of the button, add the following code: AdventureWorksEntities awe = new AdventureWorksEntities(); var query = from emp in awe.Contact where emp.FirstName.StartsWith("S") select emp; foreach (var emp1 in query) listBox1.Items.Add(emp1.LastName); By now this code should look very familiar. It looks almost exactly like the LINQ to SQL code. The first line creates an instance of the AdventureWorksEntities class, followed by a LINQ query expres- sion. The query is then iterated through and the results displayed in the list box. 804 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 805 LINQ 34 Compile the project and then run it. When the form displays, click the button. The list box will display results, some of which are shown here: Agcaoili Jacobson Altamirano Alvarado Appelbaum Ayers Querying multiple tables using LINQ to Entities and the Entity Framework This chapter closes with one last example that illustrates how to query multiple tables using LINQ to Entities and the Entity Framework. Place another button the form, and in the Click event add the following code: AdventureWorksEntities awe = new AdventureWorksEntities(); var query = from cus in awe.Customer where cus.CustomerID == 2 select cus; foreach (var cust in query) { listBox1.Items.Add(cust.CustomerID); foreach (var ord in cust.SalesOrderHeader) listBox1.Items.Add(" " + ord.OrderDate); } This example begins like the previous example, but the foreach loops are different. The LINQ query returns a single record, where the CustomerID column in the Sales table is 2. In the foreach loop, that same CustomerID is written to the list box, and then a secondary loop loops through the orders for that customerID. Compile the application and run it. When the form displays, click button2. The list box displays the CustomerID, but it does not display the sales order header rows for that customer. That is because the query fetched the customer but it did not fetch the orders. Because the Entity Framework did not know how the orders were going to be used, it did not want to automatically bring back information that was not needed. To fix that, the orders can be explicitly loaded for that specific customer by adding the following high- lighted line of code to the first foreach loop: foreach (var cust in query) { listBox1.Items.Add(cust.CustomerID); 805 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 806 Part V Data Connectivity cust.SalesOrderHeader.Load(); foreach (var ord in cust.SalesOrderHeader) listBox1.Items.Add(" " + ord.OrderDate); } When the foreach is executed, it will execute the query to return the customers, but not the orders. When the Load() statement is executed, it will create a separate query and return the orders for that customer. Run the project again and click button2. This time the list box will display the order dates for CustomerID 2: 2 8/1/2002 11/1/2002 2/1/2003 5/1/2003 8/1/2003 11/1/2003 2/1/2004 5/1/2004 The preceding query is OK if the goal is to load only orders for some of the customers (for example, when selecting a customer from a list), but in this case there exists a loop that will bring back every order for every customer. Therefore, instead of executing multiple queries, a request can be made to only pull back all of the orders with the customer in the initial query, as follows: var query = from cus in awe.Customer.Include("SalesOrderHeader") where cus.CustomerID == 2 select cus; When the application is run again, the results displayed in the text box are the same as the previous query. The goal of these two examples is to illustrate how easy LINQ to Entities and the Entity Framework are to access SQL Server and query entities. With all that was shown in this chapter, it should be very apparent how flexible yet powerful LINQ is for querying different data sources such as XML, DataSets, and Entities. Your homework assignment for this section is to create a new EF model. This time, however, in the wiz- ard use an empty model, add one or more tables to it, and then write a LINQ query to query the data. Summary The purpose of this chapter was to provide an overview of LINQ and the different LINQ providers, and show by example how powerful, flexible, and efficient they are in accessing the different types of data sources. In this chapter you learned what LINQ is and how to use LINQ to query data from different data sources, such as XML, entities, and databases, using LINQ’s powerful standard query operators. 806 www.getcoolebook.com Nielsen c35.tex V4 - 07/21/2009 2:10pm Page 807 Asynchronous Messaging with Service Broker IN THIS CHAPTER Defining a work queue Queuing messages Managing conversations S ervice Broker is a powerful yet simple work queue system that can be used to add asynchronous messaging and work queues to a database abstraction layer to provide high scalability, and it is essential in any SOA data store architecture. If you’ve ever built a table to hold work to be done, such as orders to be processed by a Materials Requirement Planning system, then you’ve built a work queue. In one application Service Broker is just that — a high-performance, wide-payload work queue integrated into SQL Server with DDL and monitoring capabilities. Service Broker can also be used to pass messages with guaranteed secure delivery between work queues, which opens up a world of possibilities. Because Service Broker is essentially just a SQL Server table, it includes all the cool transactional and back-up capabilities inherent to SQL Server. This is what sets Service Broker apart from other queuing technologies, such as Microsoft Message Queuing (MSMQ). The queue contains a single, wide column for the message body, which is OK because the message will typically contain a single XML file or fragment or SOAP message as the payload. Service Broker is not enabled by default so the first specific step to working with Service Broker is to turn it on using the ALTER DATABASE command: ALTER DATABASE AdventureWorks SET ENABLE_BROKER; 807 www.getcoolebook.com Nielsen c35.tex V4 - 07/21/2009 2:10pm Page 808 Part V Data Connectivity What’s New with Service Broker? S ervice Broker was introduced with much fanfare in SQL Server 2005. For SQL Server 2008, there are a few slight enhancements: Conversations may now have an assigned priority; there are new DMVs for monitoring Service Broker; there’s a new Service Broker Statistics Report; and Management Studio can now auto-generate some Service Broker scripts. Configuring a Message Queue Service Broker uses a messaging or dialog metaphor, but there’s much more to Service Broker than just the messages. The Service Broker objects must be defined in the following order: 1. Message types define the legal requirements of the message. 2. Contracts define the agreement between the initiating service and the target, including the message type, the queue, and the services. 3. Queues hold the lists of messages. 4. Services communicate with the queue and either send or receive messages as the initiating service or the target service, respectively. Other than defining the message type as XML and naming the objects, there isn’t much complexity to setting up a Service Broker database. That’s because the data definition language, or DDL, does all the work; Service Broker is a message-agnostic work queue that’s serving as an infrastructure for the mes- sages. There’s more work in placing messages on and taking messages off the queue. Because Service Broker is integrated within SQL Server, the objects are created using the familiar create DDL commands. The first step in creating a Service Broker queue is to define a message type and a contract that uses that message type: CREATE MESSAGE TYPE HelloWorldMessage VALIDATION = WELL_FORMED_XML ; CREATE CONTRACT HelloWorldContract ( HelloWorldMessage SENT BY INITIATOR); The initiator and target queues are also simply created using DDL: CREATE QUEUE [dbo].[TargetQueue] ; CREATE QUEUE [dbo].[InitiatorQueue] ; Likewise, the initiator and target services are also defined using DDL. Both services are associated with a queue, and the receiving, or target, service specifies that it can receive messages from a contract: 808 www.getcoolebook.com Nielsen c35.tex V4 - 07/21/2009 2:10pm Page 809 Asynchronous Messaging with Service Broker 35 CREATE SERVICE InitiatorService ON QUEUE [dbo].[InitiatorQueue]; GO CREATE SERVICE TargetService ON QUEUE [dbo].[TargetQueue] (HelloWorldContract); With the Service Broker objects created, you’ll be able to see them listed under the Object Explorer Service Broker node. Working with Conversations With the Service Broker object created, messages can be placed into the queue or received from the queue. Messages exist as part of a conversation that can be divided into conversation groups. Sending a message to the queue The following code creates a conversation using a conversationhandle GUID. The BEGIN CONVERSATION command opens the conversation, and the SEND command actually places the message into the queue: BEGIN TRANSACTION ; DECLARE @message XML ; SET @message = N‘<message>Hello, World!</message>’ ; DECLARE @conversationHandle UNIQUEIDENTIFIER ; BEGIN DIALOG CONVERSATION @conversationHandle FROM SERVICE [InitiatorService] TO SERVICE ‘TargetService’ ON CONTRACT [HelloWorldContract] WITH ENCRYPTION = OFF, LIFETIME = 1000 ; SEND ON CONVERSATION @conversationHandle MESSAGE TYPE [HelloWorldMessage] (@message) ; END CONVERSATION @conversationHandle ; COMMIT TRANSACTION ; To view the message in the queue, select from the queue table as if it were a normal relational table: SELECT CAST(message_body as nvarchar(MAX)) from [dbo].[TargetQueue] 809 www.getcoolebook.com Nielsen c35.tex V4 - 07/21/2009 2:10pm Page 810 Part V Data Connectivity Receiving a message The RECEIVE command will retrieve and remove the oldest message from the queue. Use RECEIVE within a transaction so that if something goes wrong, the receive can be rolled back and the message will still be in the queue. Service Broker is not a trigger that can code when a message is placed on the queue; some code must run to extract the message. To accomplish this, Microsoft added a new option to the WAIT FOR command, enabling it to wait for a message in the queue. Without this option, the code would have to run a loop to continuously check for a new message. The following routine within a stored procedure will wait for a message and then receive the top message from the queue: USE AdventureWorks ; GO Process all conversation groups. WHILE (1 = 1) BEGIN DECLARE @conversation_handle UNIQUEIDENTIFIER, @conversation_group_id UNIQUEIDENTIFIER, @message_body XML, @message_type_name NVARCHAR(128); BEGIN TRANSACTION ; Get next conversation group. WAITFOR( GET CONVERSATION GROUP @conversation_group_id FROM [dbo].[TargetQueue]), TIMEOUT 500 ; If there are no more conversation groups, roll back the transaction and break out of the outermost WHILE loop. IF @conversation_group_id IS NULL BEGIN ROLLBACK TRANSACTION ; BREAK ; END ; Process all messages in the conversation group. Notice that all processing occurs in the same transaction. WHILE1=1 BEGIN Receive the next message for the conversation group. Notice that the receive statement includes a WHERE clause to ensure that the messages received belong to the same conversation group. 810 www.getcoolebook.com Nielsen c35.tex V4 - 07/21/2009 2:10pm Page 811 Asynchronous Messaging with Service Broker 35 RECEIVE TOP(1) @conversation_handle = conversation_handle, @message_type_name = message_type_name, @message_body = CASE WHEN validation = ‘X’ THEN CAST(message_body AS XML) ELSE CAST(N‘<none/>’ AS XML) END FROM [dbo].[TargetQueue] WHERE conversation_group_id = @conversation_group_id ; If there are no more messages, or an error occurred, stop processing this conversation group. IF @@ROWCOUNT=0OR@@ERROR <> 0 BREAK; Show the information received. SELECT ‘Conversation Group Id’ = @conversation_group_id, ‘Conversation Handle’ = @conversation_handle, ‘Message Type Name’ = @message_type_name, ‘Message Body’ = @message_body ; If the message_type_name indicates that the message is an error or an end dialog message, end the conversation. IF @message_type_name =’http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog’ OR @message_type_name =’http://schemas.microsoft.com/SQL/ServiceBroker/Error’ BEGIN END CONVERSATION @conversation_handle ; END ; END; Process all messages in conversation group. Commit the receive statements and the end conversation statement. COMMIT TRANSACTION ; END ; Process all conversation groups. use tempdb;; 811 www.getcoolebook.com . a SQL Server table, it includes all the cool transactional and back-up capabilities inherent to SQL Server. This is what sets Service Broker apart from other queuing technologies, such as Microsoft Message. 07/21/2009 2:10pm Page 808 Part V Data Connectivity What’s New with Service Broker? S ervice Broker was introduced with much fanfare in SQL Server 2005. For SQL Server 2008, there are a few slight. conversation. IF @message_type_name =’http://schemas .microsoft. com /SQL/ ServiceBroker/EndDialog’ OR @message_type_name =’http://schemas .microsoft. com /SQL/ ServiceBroker/Error’ BEGIN END CONVERSATION