At Microsoft’s November 2009 Professional Developer Conference, the legendary Don Box, a Distinguished Engineer at Microsoft, said, “If you’re a .NET developer Entity Framework is where we’re going. We’re there. Get on board, it’s time.”
Yes, it’s time.
Developers spend far too much of their precious time worrying about their backend database, its tables and their relationships, the names and parameters of stored procedures and views, as well as the schema of the data that they return. For .NET developers, Microsoft’s new Entity Framework changes the game so that you no longer have to be concerned with the details of the data store as you write applications. You can focus on the task of writing those applications, rather than accessing the data.
The ADO.NET Entity Framework has shifted into becoming Microsoft’s core data access platform for building .NET applications. It was released in July 2008 as part of the Visual Studio 2008 Service Pack 1 and .NET 3.5 Service Pack 1, two years after Microsoft announced it at its TechEd 2006 Conference. As a version 1 product, Entity Framework was greeted at first with skepticism, and its adoption was far from sweeping. However, with the release of Visual Studio 2010 and .NET 4 in April 2010, a much improved Entity Framework finally got the attention and excited responses of many developers and .NET teams, who are now quickly jumping aboard.
Although ADO.NET retains its existing data access, as Microsoft’s core data access strategy going forward the Entity Framework will receive the bulk of the innovation and resources from the Business Platform Division (which owns all of the data programmability tasks at Microsoft). It’s an important technology for Microsoft, and one that you should not ignore. Entity Framework is also being integrated into many of Microsoft’s products, whether the product uses Entity Framework to support its own features, such as with Commerce Server 2009’s Multi-Channel Commerce Foundation,[1] or whether the product has support for interacting with the Entity Framework, such as with SQL Server Modeling.
Why do we need a new data access technology? After forcing developers to switch from one data access technology to another—from DAO to RDO to ADO and then to ADO.NET—with ADO.NET Microsoft seemed to have finally settled on a single tool in which developers could invest. With each release of Visual Studio and the .NET Framework, ADO.NET has been enhanced and added to, but has remained backward compatible all along. Our investment has been safe.
And it remains safe, even though it will be stagnant. The Entity
Framework is another enhancement to ADO.NET, giving developers an added
mechanism for accessing data and working with the results in addition to
DataReader
s and DataSet
s.
But Microsoft went as far as it could with the DataSet
paradigm. The next step was to enable
developers to focus on a domain model while .NET would automate the
redundant tasks of database interaction.
In this chapter, you will learn about the critical pieces of the
Entity Framework, the Entity Data Model, entity classes, the core .NET
APIs, and Visual Studio design tools. You will also learn about how Entity
Framework fits in with ADO.NET’s DataSet
s and LINQ to SQL. Finally, you will
learn about many of the changes and additions to Entity Framework in
Visual Studio 2010 and .NET 4, and how so many of the pain points in the
first version have been eliminated.
A central benefit of the Entity Framework is that it frees you from being concerned with the structure of your database. All of your data access and storage is done against a conceptual data model that reflects your own business objects.
With ADO.NET DataReader
s and
many other data access technologies, you spend a lot of time writing
code to get data from a database, read the results, pick out bits of
data you want, and push them into your business classes. With the Entity
Framework, you no longer query against the schema of a database, but
rather against a schema that reflects your own business model. As data
is retrieved, you are not forced to reason out columns and rows and push
them into objects, because they are returned as objects. When it’s time
to save changes back to the database, you have to save only those
objects. The Entity Framework does the necessary work of translating
your objects back into the rows and columns of the relational store. The
Entity Framework does this part of the job for you, similar to the way
an Object Relational Mapping (ORM) tool works.
The Entity Framework uses a model called an Entity Data Model (EDM), which evolved from Entity Relationship Modeling (ERM), a concept that has been used in database development for many years.
An Entity Data Model (EDM) is a client-side data model and it is the core of the Entity Framework. It is not the same as the database model, which belongs to the database. The data model in the application describes the structure of your business objects. It’s as though you were given permission to restructure the database tables and views in your enterprise’s database so that the tables and relationships look more like your business domain rather than the normalized schema that is designed by database administrators.
Figure 1-1 shows the
schema of a typical set of tables in a database. PersonalDetails
provides additional
information about a Person
that the
database administrator has chosen to put into a separate table for the
sake of scalability. SalesPerson
is a
table that is used to provide additional information for those who are
salespeople.
Working with this data from an application requires queries that
are full of inner joins and outer joins to access the additional data
about Person
records. Or you will
access a variety of predefined stored procedures and views, which might
each require a different set of parameters and return data that is
shaped in a variety of ways.
A T-SQL query to retrieve a set of SalesPerson
records along with their personal
details might look something like this:
SELECT SalesPerson.*, PersonalDetails.*, Person.* FROM Person INNER JOIN PersonalDetails ON Person.PersonID = PersonalDetails.PersonID INNER JOIN SalesPerson ON Person.PersonID = SalesPerson.PersonID
Imagine that a particular application could have its own view of what you wish the database looked like. Figure 1-2 reshapes the schema.
All of the fields from PersonalDetails
are now part of Person
. And SalesPerson
is doing something that is not
even possible in a database: it is deriving from Person
, just as you would in an object
model.
Now imagine that you can write a LINQ query that looks like this:
from p in People.OfType<SalesPerson>() select p
In return, you will have a set of SalesPerson
objects with all of the properties
defined by this model (see Figure 1-3).
Note
LINQ exists only in the C# and Visual Basic languages. With the Entity Framework there is another way to express queries, which not only allows you to use other languages, but also provides additional benefits that you can take advantage of as necessary. It’s called Entity SQL, and you will learn much more about it and LINQ to Entities in Chapters 3 through 5.
This is the crux of how the Entity Framework can remove the pain of having not only to interact with the database, but also to translate the tabular data into objects.
.NET is but one tool that uses an EDM. The next version of SQL Server will use an EDM for Reporting Services and you will soon begin to see other Microsoft applications that will adopt the EDM concept as well. In fact, you will find that model-driven development in general is getting more and more attention from Microsoft.
When working with the Entity Framework, you will implement an EDM that is particular to the Entity Framework. In the Entity Framework, an EDM is represented by a single XML file at design time that is split into a set of three XML files at runtime, only one of which represents a conceptual model. The other two provide metadata that enables Entity Framework to interact with a database. You’ll learn much more about this metadata in Chapter 2.
The items described by an EDM are known as entities. Classes that are generated from the model entities, along with their instantiated object, are also referred to as entities but are often called entity classes or entity objects. The generated entity classes differ from typical business classes in that they have properties but no behavior apart from methods to enable change tracking.
Figure 1-4 shows
the class diagram for the Person
and
SalesPerson
classes that the model
shown in Figure 1-2
would generate automatically. Each class has a factory method (e.g.,
CreatePerson) as well as methods used to notify the Entity Framework
when a property changes.
With the classes the Entity Framework generates, you can add your own business logic, pull the results into business objects of your own, and even link your business objects to the EDM, replacing the generated classes. But by definition, the entity classes describe only their schema.
In addition to being able to reshape the entities in a data model
as with the inheritance hierarchy shown in Figure 1-2, you can define
relationships between entities. Figure 1-5 adds a Customer
entity to the model which also
derives from Person
as well as an
Order
entity. Notice the relationship
lines between SalesPerson
and
Order
, showing a one-to-many
relationship between them. There is also a one-to-many relationship
between Customer
and Order
.
When you write queries against this version of the model, you
don’t need to use JOIN
s. The model
provides navigation between the entities.
The following LINQ to Entities query retrieves order information
along with information about the customer. It navigates into the
Customer
property of the Order
to get the FirstName
and LastName
of the Customer
.
from o in context.Orders select new {o.OrderID,o.OrderNumber,o.Customer.FirstName,o.Customer.LastName}
Once that data is in memory, you can navigate through each object
and its properties, such as myOrder.Customer.LastName
, just as
readily.
The Entity Framework also lets you retrieve graphs, which means
you can return shaped data such as a Customer
with all of its Order
details already attached.
These are some of the major benefits to querying against a data model, rather than directly against the database.
You may have noticed that I have not mentioned the actual data store that owns the data being queried. The model doesn’t have any knowledge of the data store—what type of database it is, much less what the schema is. And it doesn’t need to.
The database you choose as your backend will have no impact on your model or your code.
The Entity Framework communicates with the same ADO.NET data providers that ADO.NET already uses, but with a caveat. The provider must be updated to support the Entity Framework. The provider participates in reshaping the Entity Framework’s queries and commands into native queries and commands. All you need to do is identify the provider and a database connection string so that the Entity Framework can get to the database.
This means that if you need to write applications against a number of different databases, you won’t have to learn the ins and outs of each database. You can write queries with the Entity Framework’s syntax (either LINQ to Entities or Entity SQL) and never have to worry about the differences between the databases. If you need to take advantage of functions or operators that are particular to a database, Entity SQL allows you to do that as well.
Microsoft’s SqlClient
APIs
that are included with Visual Studio 2008 SP1 and Visual Studio 2010
support the Entity Framework. They will allow you to use SQL Server
2000, 2005, and 2008. You can use the full or Express version of SQL
Server 2005 and 2008 and the full version of SQL Server 2000. Note
that the Entity Data Model Designer cannot interact with SQL Server
2000. This is not a limitation of Entity Framework’s design tools but
Visual Studio 2010 itself. None of Visual Studio’s tools recognizes
SQL Server 2000. However, the Entity Framework runtime can. SQL Server
CE version 3.5 and 4 support the Entity Framework as well. Check out
the July 7, 2010, blog post from the SQL Server CE team about SQL
Server Compact 4 at http://blogs.msdn.com/sqlservercompact.
At the time of this writing, a host of other providers are available—and more are on the way—that will allow you to use Oracle, IBM databases, SQL Anywhere, MySQL, SQLite, VistaDB, and many other databases. The providers are being written by the database vendors as well as by third-party vendors. Many of these providers were written for .NET 3.5. There is only one critical feature that they will not support until they have been updated to .NET 4: a feature called model first, which you will learn about in Chapter 25.
Note
Microsoft lists providers on the “ADO.NET Data Providers” page of the Data Developer Center at http://msdn.microsoft.com/en-us/data/dd363565.aspx.
Note
Microsoft provides guidance for developers who want to build Entity Framework support into their database providers. I will not be covering this topic in this book. You can see EF team blog posts about writing providers at http://blogs.msdn.com/b/adonet/archive/tags/sample+provider.
A provider that supports the Entity Framework needs to have specific knowledge about the type of database it is connecting to. It needs to be aware of the available functions and operators for the database, as well as the proper syntax for native queries. Open Database Connectivity (ODBC) providers provide generic access to a variety of databases, including Access, and cannot furnish the necessary database particulars to act as a provider for the Entity Framework. Therefore, ODBC is not a valid provider for the Entity Framework. Unless someone creates a provider specifically for Access, you won’t be able to use it with Entity Framework applications. Microsoft does not have plans to build an Access provider, because the demand is too low.
In addition to the EDM, the Entity Framework provides a set of .NET runtime APIs that let you write .NET applications using the EDM. It also includes a set of design tools for designing the model. Following is a synopsis of the Entity Framework’s key features.
Although the Entity Framework is designed to let you work directly with the classes from the EDM, it still needs to interact with the database. The conceptual data model that the EDM describes is stored in an XML file whose schema identifies the entities and their properties. Behind the conceptual schema described in the EDM is another pair of XML files that map your data model back to the database. One is an XML file that describes your database and the other is a file that provides the mapping between your conceptual model and the database.
During query execution and command execution (for updates), the Entity Framework figures out how to turn a query or command that is expressed in terms of the data model into one that is expressed in terms of your database. It does this by reading the metadata.
When data is returned from the database, it uses the metadata to shape the database results into the entities and further materializes objects from those results.
Entity Framework acquires the ability to use an in-memory model with a feature called code first that is part of the Entity Framework Community Technical Preview (CTP). It is not yet part of Entity Framework and must be downloaded separately. Code first allows you to work solely with classes, and the necessary XML metadata is generated in memory on the fly at runtime. You’ll learn more about this feature in Chapter 25, but be aware that at the time of this book’s publication, code first is still a CTP and is not yet fully developed.
The screenshots in Figures 1-2 and 1-3 are taken from the EDM Designer. It is part of Visual Studio and provides you with a way to work visually with the model rather than tangle with the XML. You will work with the Designer right away in Chapter 2, and you’ll learn how to use it to do some more advanced modeling, such as inheritance, in Chapter 14 and “model-first” design in Chapter 25. You will also learn about the Designer’s limitations, such as the fact that it does not support all of the features of the EDM. With some of the less frequently used EDM features, you’ll have to work directly with the XML after all. In Chapter 2, you will get a look at the XML and how it relates to what you see in the Designer so that when it comes time to modify it in Chapter 15, you’ll have some familiarity with the raw schema files.
Note
In Visual Studio 2010, the Designer supports many more features than it did in Visual Studio 2008 SP1. However, as you will see in Chapters 14 and 15, there are still some things you will need to do manually.
The Designer also allows you to map stored procedures to entities, which you’ll learn about in Chapter 6. If you are coming from Visual Studio 2008 SP1, you’ll find that the Designer’s stored procedure support has been greatly improved in Visual Studio 2010.
Another notable feature of the Designer is that it will let you update the model from the database to add additional database objects that you did not need earlier or that have been added to the database since you created the model.
One of the EDM design tools is the Entity Data Model Wizard. It allows you to point to an existing database and create a model directly from the database so that you don’t have to start from scratch. Once you have this first pass at the model, you can begin to customize the model in the Designer.
Not every development project begins with a legacy database. One of the new features in Visual Studio 2010 is the ability to create a model directly in the Designer and then generate database schema based on that model. Although we’ll focus on a model created using database-first design through most of this book, you’ll get a chance to drill into model-first design, as well as some additional Designer features, in Chapter 25.
Once you have a model to define your domain entities, you’ll need classes to represent them at runtime. The Designer automatically generates those classes for you from the model. However, we’ve gained another critical feature in Visual Studio 2010. Rather than using the proprietary code generator that was written for Entity Framework in Visual Studio 2008 SP1, the classes are generated using Visual Studio’s Text Template Transformation Toolkit (T4). Not only does this provide a more commonly known mechanism for code generation, but also you can much more easily customize the provided templates to define exactly how you would like classes to be generated from your model. You’ll learn about the code generation capabilities beginning with Chapter 11 and work further with T4 customization in later chapters. There are some scenarios where you will be able to skip code generation completely and simply use your own classes.
The Entity Framework runtime’s most prominent feature set and
that which you are likely to work with most often is referred to as
Object Services. Object Services sits on top of the Entity Framework
stack, as shown in Figure 1-6, and
provides the functionality needed to work with objects that are based
on your entities. Object Services provides a class called EntityObject
and can easily manage any class
that inherits from EntityObject
.
This includes materializing objects from the results of queries
against the EDM, keeping track of changes to those objects, managing
relationships between objects, and saving changes back to the
database.
In between querying and updating, Object Services provides a host of capabilities to interact with entity objects, such as automatically working with a lower level of the Entity Framework to do all of the work necessary to make calls to the database and deal with the results. Object Services also provides serialization (both XML and binary).
One of the most important runtime enhancements in .NET 4 is the
ability for the Entity Framework to manage entities that do not
inherit from EntityObject
. This is
Entity Framework’s new POCO (Plain Old CLR Objects) support, and you
will learn much more about this feature beginning with Chapter 13. POCO support is
critical to enable a variety of different programming styles. With
POCO entities, developers can more easily build unit tests as well as
persistent ignorant entity classes. These capabilities are crucial to
developers who follow the coding patterns recommended by domain-driven
and agile development. These same developers were unable to use the
Entity Framework in .NET 3.5. Now Entity Framework embraces a wider
population of the development community.
Once an entity object has been instantiated, either as a result of data returned from a query or by instantiating a new object in code, Object Services can keep track of that object. This is the default for objects returned from queries. When Object Services manages an object, it can keep track of changes made to the object’s properties or its relationships to other entity objects.
Object Services then uses the change-tracking information when
it’s time to update the data. It constructs Insert
, Update
, and Delete
commands for each object that has
been added, modified, or deleted by comparing the original values to
the current values of the entity. If you are using stored procedures
in conjunction with entities it will pass the current values (and any
original values specifically identified) to those procedures.
Relationships are a critical piece of the EDM; however, in .NET 4, an important new feature was added: foreign key support. In a classic Entity Relationship Model, foreign keys are not exposed in the conceptual model. Entity Framework followed this paradigm in .NET 3.5 SP1, but we developers still wanted access to those foreign key values for many reasons. In fact, in the first edition of this book, I showed a variety of ways to go “under the covers” to get and set foreign keys. Now Entity Framework supports having the foreign keys in the conceptual model. However, for backward compatibility, you can still use the former mechanism which, because of the lack of foreign keys, instantiates relationships as objects.
Note
I will not spend a lot of time focusing on the older style of building relationships in this book as it is not the default and will be used minimally. If you need in-depth guidance on how to work with relationships when the foreign key is not available in the conceptual model, I recommend that you read the first edition of this book.
As you will find, especially in Chapter 19, which dives deep into relationships, even with foreign keys, you will need to have a very good understanding of how relationships work. Some of the rules of engagement when working with related data are not very intuitive, and you can write code that will raise plenty of exceptions, or worse, will return invalid results, if you break these rules. Chapter 19 will provide insight into relationships in the EDM so that you will be able to work with them in an expert manner.
You can use entities in many .NET data binding scenarios. In
Windows Forms and WPF, you can use entities as a data source for
data-bound controls or as the data source for BindingSource
controls, which orchestrate
the binding between objects and UI controls on the form. Chapter 9 provides a
well-informed walkthrough for using entities with BindingSource
controls to edit and update
data. Chapter 26
focuses on separating the data access and other business logic from
the user interface to provide better architecture for your
applications.
Chapter 9 also provides a walkthrough for data-binding entities in Windows Presentation Foundation (WPF) applications. Visual Studio 2010 introduced a host of enhancements for data binding in WPF, and you’ll benefit greatly from these when data-binding with entities.
For ASP.NET, there is a DataSource
control called EntityDataSource
that works in a similar way
to the ASP.NET SqlDataSource
and
LinqDataSource
controls, allowing
you to declaratively bind entity objects to your user interface. Chapter 12 is all about
using EntityDataSource
. You’ll also get a
quick look at binding with ASP.NET Dynamic Data in that
chapter.
Entity Framework made significant advancements for n-tier development in .NET 4. In .NET 3.5 SP1, it was just too hard; as such, in the previous edition of this book I devoted a lot of pages to hacks and workarounds. Now we can benefit greatly from not only the foreign keys but also a slew of state management methods that make working across processes much simpler. Additionally, POCOs make n-tier development easier to achieve as you’ll see in the final chapters of this book.
For layered applications, Chapter 24 and Chapter 25 focus on pulling all of the data access tasks out of the ASP.NET user interface, and you’ll see a WPF application, an ASP.NET Web Forms application, and an ASP.NET MVC application using various patterns to separate your logic.
EntityClient
is the other
major API in the Entity Framework, though one that you are less likely
to work with directly. It provides the functionality necessary for
working with the store queries and commands (in conjunction with the
database provider) connecting to the database, executing the commands,
retrieving the results from the store, and reshaping the results to
match the EDM.
You can work with EntityClient
directly or work with Object
Services, which sits on top of EntityClient
. Not only is EntityClient
able to perform queries, but it
does this on behalf of Object Services. The difference is that when
you work directly with EntityClient
, you will get tabular
results (though the results can be shaped). If you are working with
Object Services, it will transform the tabular data created by
EntityClient
into objects.
The tabular data returned by EntityClient
is read-only. EntityClient
is well suited for reporting
and moving data from one persistence mechanism to another. Only Object
Services provides change tracking and the ability to save changes back
to the data store.
You can use the Entity Framework anywhere you can use ADO.NET,
including web services and WCF services. Chapters 17 and 18 walk you through the process of
providing services for EntityObject
entities and POCO entities.
In these chapters, we’ll also take a look at WCF Data Services, WCF RIA Services, and a specialized POCO template called Self-Tracking Entities, which provides client-side change-tracking capabilities to entities, thereby allowing a simpler way to send changes to WCF services and then persist them to the database.
The Entity Framework is only the latest addition to the ADO.NET
stack. How does that impact existing code that uses DataSet
s and DataReader
s or LINQ to SQL? Can you continue
to write new code using these technologies?
DataSet
s and DataReader
s are not going away. All of your
existing investment will continue to function and you can continue to
use this methodology of retrieving data and interacting with it. The
Entity Framework provides a completely different way to retrieve and
work with data. You would not integrate the two technologies—for
example, using the Entity Framework to query some data, and then
pushing it into a DataSet; there would be no point. You should use one
or the other. As you learn about the Entity Framework, you will find
that it provides a very different paradigm for accessing data. You may
find that the Entity Framework fits for some projects, but not others,
where you may want to stick with DataSet
s.
The Entity Framework uses DataReader
s as well in the form of an
EntityDataReader
, which inherits
the same DbDataReader
as SqlDataReader
. This is what a query with
EntityClient
returns. In fact,
you’ll find that the code querying the EDM with EntityClient
looks very
similar to the code that you use to query the database directly with
ADO.NET. It uses connections, commands, and command parameters, and
returns a DbDataReader
that you can
read as you would any other DataReader
, such as SqlDataReader
.
Some ADO.NET tools that are not available with the Entity
Framework are query notification and ASP.NET’s SqlCacheDependency
. Additionally, ADO.NET’s
SqlBulkCopy
requires a DataReader
or DataSet
to stream data into the database;
therefore, you cannot do client-side bulk loading with the Entity
Framework. The Entity Framework does not have an equivalent to
ADO.NET’s DataAdapter.BatchUpdate
.
Therefore, when the Entity Framework saves changes to the database, it
can send only one command at a time.
LINQ to SQL and the Entity Framework look similar on the surface. They both provide LINQ querying against a database using a data model.
A frequently asked question is: why did Microsoft create two similar technologies? LINQ to SQL evolved from the LINQ project, which came out of team working with language development. The Entity Framework was a project of the Data Programmability team and was focused on the Entity SQL language. By the time each technology had come along far enough that it was being shown to other teams at Microsoft, it was clear that Microsoft had two great new technologies that could target different scenarios. The Entity Framework team adapted LINQ to work with entities, which confused developers even more because LINQ to Entities and LINQ to SQL look so much alike.
LINQ to SQL eventually was brought into Microsoft’s Data Programmability team, and in November 2008 the team announced that because the technologies target the same problems, going forward they would focus on developing the Entity Framework, which supports multiple databases and aligns with many of Microsoft’s upcoming technologies through its use of an Entity Data Model. However, they will continue to maintain and tweak LINQ to SQL. This is not a happy situation for many developers who have made an investment in LINQ to SQL. Microsoft is committed to maintaining LINQ to SQL in ADO.NET and has made no statements regarding deprecating it. It has also promised to provide a migration path from LINQ to SQL to the Entity Framework and will recommend the Entity Framework over LINQ to SQL in future programmer guidelines.
In the first edition of this book, Chapter 1 listed two pages of pain points. Their section titles were “The Entity Framework Designer” (which focused on the lack of support for stored procedures and other EDM features), “Challenges with Change Tracking Distributed Applications,” “Domain-Driven Development,” and “Unit Testing.” I’m very happy to have removed every one of these sections thanks to the great improvements that have been made in .NET 4 and Visual Studio 2010.
There are still definitely a lot of nits to pick, however. The model would benefit from support for things such as unique foreign keys, table-valued functions, enhanced many-to-many relationships, and a problem that is much more than a nit: support for very large models.
Entity Framework’s state management and relationship management still have a lot of behavior that is not intuitive and will certainly bite you if you don’t study up on it. Take a look at Chapter 19 for a good study guide.
This book spends plenty of time looking into the depths of the Entity Framework runtime to show you how to get around some of these limitations, and attempts to point out potholes, hiccups, and omissions.
Users of more mature ORM tools continue to have complaints about Entity Framework as well, such as the difficulty of providing internal transactions (database transactions, however, are supported). But if you look around the marketplace, even Entity Framework’s staunchest competitors are getting on board and leveraging their experience to provide advanced tools for working with Entity Framework. The principal player, NHibernate, created a wonderful database profiling tool for Entity Framework, and LLBLGen Pro has built a powerful designer for Entity Framework that takes a very different approach for managing an Entity Framework EDM and its metadata (http://www.llblgen.com).
As you read through this book, you will gain experience in designing EDMs and using the Entity Framework to write applications, as well as dig deep into the APIs to learn how to manipulate entity objects and have granular control over much of their behavior. A lot of functionality is very accessible, and there’s a lot of hidden power. You will learn what’s under the covers so that you can realize the true benefits of the Entity Framework.
Even as I wrap up this edition of Programming Entity Framework, I look forward to future versions of the framework as it continues to evolve.
Get Programming Entity Framework, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.