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 procedure
Trang 1LINQ 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
Trang 2connection 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 theObjectContext
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 theapp.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 thePerson.Contact,HumanResources.Employee,Sales.Customer, and
Sales.SalesOrderHeadertables
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 toAdventureWorksModelin
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 calledAdventureWorks.edmxand it has a child file, which is theDesignerfile called
AdventureWorks.Designer.cs
Opening the edmxfile opens the visual model designer that was created at the end of the wizard
Our focus here is theDesigner.csfile Opening that file displays some code that should look very
familiar TheDesigner.csfile contains theDataContextand 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 theDataContext:
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 theEntityObjectclass, 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
Trang 3The class is also attributed with several attributes, such asAdmEntityTypeAttribute, 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 theEdmScalarProperty
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 TheEdmRelationship
Attributeis 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 theAdventureWorksEntitiesclass, followed by a LINQ query
expres-sion The query is then iterated through and the results displayed in the list box
Trang 4Compile 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 theforeachloops are different The LINQ query
returns a single record, where the CustomerID column in theSalestable is 2 In theforeachloop,
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 firstforeachloop:
foreach (var cust in query)
{
listBox1.Items.Add(cust.CustomerID);
Trang 5foreach (var ord in cust.SalesOrderHeader) listBox1.Items.Add(" " + ord.OrderDate);
}
When theforeachis executed, it will execute the query to return the customers, but not the orders
When theLoad()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
Trang 6Asynchronous Messaging
with Service Broker
IN THIS CHAPTER
Defining a work queue Queuing messages Managing conversations
Service 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 theALTER DATABASEcommand:
ALTER DATABASE AdventureWorks SET ENABLE_BROKER;
Trang 7What’s New with Service Broker?
Service 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:
Trang 8CREATE 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 aconversationhandleGUID TheBEGIN
CONVERSATIONcommand opens the conversation, and theSENDcommand 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]
Trang 9Receiving a message
TheRECEIVEcommand will retrieve and remove the oldest message from the queue UseRECEIVE
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 theWAIT FORcommand, 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
WHILE 1 = 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
Trang 10TOP(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 = 0 OR @@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;;