SlideShare a Scribd company logo
Entity Framework 6 Recipes 2nd Edition Brian
Driscoll download
https://ebookgate.com/product/entity-framework-6-recipes-2nd-
edition-brian-driscoll/
Get Instant Ebook Downloads – Browse at https://ebookgate.com
Get Your Digital Files Instantly: PDF, ePub, MOBI and More
Quick Digital Downloads: PDF, ePub, MOBI and Other Formats
Entity Framework 4 in Action Stefano Mostarda
https://ebookgate.com/product/entity-framework-4-in-action-
stefano-mostarda/
Entity Framework Core in Action Second Edition Jon P
Smith
https://ebookgate.com/product/entity-framework-core-in-action-
second-edition-jon-p-smith/
Web Development Recipes 2nd Edition Edition Brian P.
Hogan
https://ebookgate.com/product/web-development-recipes-2nd-
edition-edition-brian-p-hogan/
WCF 4 5 Multi Layer Services Development with Entity
Framework Third Edition Mike Liu
https://ebookgate.com/product/wcf-4-5-multi-layer-services-
development-with-entity-framework-third-edition-mike-liu/
WCF 4 5 Multi Layer Services Development with Entity
Framework 3rd New edition Edition Liu Mike
https://ebookgate.com/product/wcf-4-5-multi-layer-services-
development-with-entity-framework-3rd-new-edition-edition-liu-
mike/
Web Development Recipes Second Edition Brian P. Hogan
Et Al.
https://ebookgate.com/product/web-development-recipes-second-
edition-brian-p-hogan-et-al/
Web Based Training Creating e Learning Experiences 2nd
Edition Margaret Driscoll
https://ebookgate.com/product/web-based-training-creating-e-
learning-experiences-2nd-edition-margaret-driscoll/
Schwarz Christoffel Mapping 1st Edition Tobin A.
Driscoll
https://ebookgate.com/product/schwarz-christoffel-mapping-1st-
edition-tobin-a-driscoll/
Database Design Using Entity Relationship Diagrams
Second Edition Bagui
https://ebookgate.com/product/database-design-using-entity-
relationship-diagrams-second-edition-bagui/
Entity Framework 6 Recipes 2nd Edition Brian Driscoll
For your convenience Apress has placed some of the front
matter material after the index. Please use the Bookmarks
and Contents at a Glance links to access them.
v
Contents at a Glance
About the Authors
����������������������������������������������������������������������������������������������������������� xxvii
About the Technical Reviewer����������������������������������������������������������������������������������������� xxix
Preface���������������������������������������������������������������������������������������������������������������������������� xxxi
Chapter 1: Getting Started with Entity Framework
■
■ �����������������������������������������������������������1
Chapter 2: Entity Data Modeling Fundamentals
■
■ ��������������������������������������������������������������11
Chapter 3: Querying an Entity Data Model
■
■ ����������������������������������������������������������������������55
Chapter 4: Using Entity Framework in ASP.NET MVC
■
■ 
�����������������������������������������������������107
Chapter 5: Loading Entities and Navigation Properties
■
■ �������������������������������������������������129
Chapter 6: Beyond the Basics with Modeling and Inheritance
■
■ 
��������������������������������������183
Chapter 7: Working with Object Services
■
■ ����������������������������������������������������������������������235
Chapter 8: Plain Old CLR Objects
■
■ �����������������������������������������������������������������������������������257
Chapter 9: Using the Entity Framework in N-Tier Applications
■
■ �������������������������������������295
Chapter 10: Stored Procedures
■
■ �������������������������������������������������������������������������������������341
Chapter 11: Functions
■
■ ���������������������������������������������������������������������������������������������������375
Chapter 12: Customizing Entity Framework Objects
■
■ �����������������������������������������������������413
Chapter 13: Improving Performance
■
■ �����������������������������������������������������������������������������451
Chapter 14: Concurrency
■
■ �����������������������������������������������������������������������������������������������483
Index���������������������������������������������������������������������������������������������������������������������������������503
1
Chapter 1
Getting Started with Entity Framework
When working with relational databases, we think in terms of tables with rows and columns. Tables are highly
structured and excel at set-based processing. Before the wide adoption of object-oriented programming, we thought
about problems “procedurally” and solved them by writing code in a structured, top-down manner, function after
function. Both worlds lined up well: Tables, rows, and columns closely matched the structured and procedural
patterns in our code. Life was good - for a time...
Much has evolved on the code side. We now think in terms of objects and domain models. We architect,
design, and program against real-world things, like customers and orders. We draw nouns in our problem space
on whiteboards. We draw lines between them, denoting relationships and interactions. We build specifications
and assign work to development teams in terms of these drawings. In short, we architect, design, and program at a
conceptual level that is very distant from the logical and physical organization of the database.
While the software development process has dramatically matured and the way in which we reason and
solve problems has evolved, the database has not. The data remains locked in the same tables, rows, and columns
paradigm, where it has been for many years. Unfortunately, this creates a mismatch (an impedance mismatch, as
Microsoft fellow Anders Hejlsberg might call it): Object-oriented class hierarchies vs. a highly normalized database
structure.
To cope with this gap, software projects often introduce a “database layer” that translates application domain
classes into the rows and columns saved in tables. This approach has spawned many commercial and open-source
data access frameworks; all attempting to bridge the ever widening gap between evolving development processes and
structured data. Interestingly, an entire new field of Object Relational Mapping (ORM) has come out it.
The Entity Framework, coupled with the Language-Integrated Query (LINQ) framework, both from Microsoft,
enables us to address the mismatch problem head-on. Using Entity Framework, we model entity classes for our
application on a design surface or directly in code. Then we model relationships (associations) between these entities.
In our code, we construct LINQ queries to program against these entities and associations. LINQ allows us to express
relational database set concepts directly into our code while working in terms of entity types and associations. All
of this helps to streamline our development experience while reducing the overall effort. Instead of coding large
numbers of highly redundant ADO.NET data access constructs, we express our data needs in simple LINQ queries.
Instead of programming against the schema of a highly normalized database, we code against entity classes. Entity
Framework maps entity classes to the underlying database for you.
Note
■
■ We use the term entity class or entity object to refer to a class that typically represents a domain item in an
application. Domain classes represent real-world objects, such as an Employee, Department, or Manager, which your
application will represent and track. The end users and stakeholders of your application should be able to look at the
domain classes in your application and say, “Yes, that’s what our business does.” Entity classes define the schema,
or properties, but not the behavior, of a domain class. In essence, entity classes expose the state of an object.
Chapter 1 ■ Getting Started with Entity Framework
2
1-1. A Brief Tour of the Entity Framework World
Entity Framework is Microsoft’s strategic approach to data access technology for building software applications.
Unlike earlier data access technologies, Entity Framework, coupled with Visual Studio, delivers a comprehensive,
model-based ecosystem that enables you to develop a wide range of data-oriented applications, including desktop,
Internet, cloud, and service-based applications, many of which will be covered in this book.
The History
Entity Framework is not new. The product dates back to Visual Studio 2008 and has come a long way in features and
functionality. Figure 1-1 gives the pictorial history.
The first version of Entity Framework was limited, featuring basic ORM support and the ability to implement
a single approach known as Database First, which we thoroughly demonstrate in this book. Version 4 brought us
another approach to using Entity Framework: Model First, along with full Plain Old CLR Object (POCO) support and
default lazy loading behavior. Soon after, the Entity Framework team released three smaller, or point releases, 4.1
through 4.3, which represented yet another approach to using Entity Framework: Code First. As shown above,
Version 5 of Entity Framework coordinated with the release of the .NET 4.5 framework and Visual Studio 2012,
delivering significant performance improvements along with support for enums, table value functions, spatial types,
the batch import of stored procedures, and deep support with the ASP.NET MVC framework.
Now we are at Version 6 of the Entity Framework. Version 6 delivers asynchronous support for querying and
updates, stored procedure support for updates in Code First, improved performance, and a long list of new features,
which we will focus on in this book.
Figure 1-1. A short history of the Entity Framework
Chapter 1 ■ Getting Started with Entity Framework
3
Note
■
■ Version 5 of Entity Framework can also be used with Visual Studio 2010. Version 6 of Entity Framework, released
with Visual Studio 2013, has tooling/runtime support for Visual Studio 2012 and runtime support for Visual Studio 2010.
To level set, let’s take a brief look at some of the key components of the Entity Framework ecosystem. What
follows is not by any means a comprehensive description of Entity Framework; that would take hundreds of pages.
We’ll look at just a few key areas to help get you oriented for the recipes that are at the heart of this book.
The Model
Entity Framework is a technology with a strong focus on modeling. As you model with Entity Framework, you will
see many familiar genetic markers from previous technologies and patterns. For example, you will, no doubt, see a
resemblance to entity-relationship diagrams and the widely adopted conceptual, logical, and physical design layering
approach.
The model that you create in Entity Framework is characterized by a construct called an Entity Data Model
(EDM), which enables you to code against strongly typed entity classes, not database schema and objects. (Figure 1-2
shows this model in conceptual form.) The Entity Data Model enables you to customize the mappings between entity
classes and database tables to move beyond the classic, one-to-one mapping, or class-to-table mapping.
In Figure 1-2, note how the database tables (on the left) do not directly map to the entity classes, which we code
against (on the right). Instead, the mapping capabilities built into the Entity Data Model enable the developer to code
against a set of entity classes that more closely resemble the problem domain, as opposed to a highly normalized
database, designed for performance, scalability, and maintainability.
For example, note above how the Employees, Devices, and Phone numbers) are physically stored in three
different tables, which from a DBA perspective makes perfect sense. But the developer codes against a single
Employee entity class that contains a collection of Devices and Phone Numbers. From a developer and project
Figure 1-2. The Entity Data Model
Chapter 1 ■ Getting Started with Entity Framework
4
stakeholder perspective, an employee is a single object, which happens to contain phone numbers and devices.
The developer is unaware, and does not care, that the DBA has normalized this employee object into three separate
database tables. Once configured, the mapping between the single class and three database tables is abstracted away
and handled by the Entity Framework.
A reverse situation can be seen for the single Department table, which programmatically maps to three entity
classes that represent individual departments. Again, to the developer and project stakeholders, a separate entity
object represents each department (Accounting, Marketing, Finance, and so on), but DBA optimizes and collapses
these objects into a single database table for data storage purposes.
Of course, as can be seen in the Location table, you can easily map a single entity class to a single database table,
which is the default behavior for Entity Framework.
The key takeaway here is that developer and project stakeholders work with a representation of domain classes
that make sense in the context of the application. The DBA can structure the underlying database tables in order to
efficiently tune the database. And you can easily bridge these two worlds with the Entity Framework.
The Layers
The Entity Data Model consists of three separate layers: the conceptual, store, and mapping layers. Each layer is
decoupled from the others.
The entity classes are contained in the conceptual layer of the Entity Data Model. This is layer in which developers
and project stakeholders work. Depending upon how you implement the Entity Framework, the conceptual layer can
be modeled with a designer or from code. Once you make that decision, you can reverse- engineer your model from
an existing database, leveraging the designer and extensive tooling that ships with Entity Framework or create your
model with code and have Entity Framework generate the database for you. The syntax for the conceptual layer is
defined in the Conceptual Schema Definition Language (CSDL).
Every useful application needs to persist objects to some data store. The store layer of the Entity Data Model
defines the tables, columns, relationships, and data types that map to the underlying database. The Store Schema
Definition Language (SSDL) defines the syntax for the store model.
Finally, the mapping layer defines the mapping between the conceptual and store layer. Among other things,
this layer defines how properties from entity classes map to columns in database tables. This layer is exposed to the
developer from the Mapping Details window contained in the Entity Framework designer or data annotations and
fluent API if choosing a code-based approach. The Mapping Specification Language (MSL) defines the syntax for the
mapping layer.
The Terminology
As expected, the Entity Framework comes with its own vocabulary. If you have used any of the popular ORM tools
or are familiar with database modeling, you’ve probably encountered some of the terminology before. Although the
entire vocabulary is extensive, we’ll provide just a few of the basic terms to get us started.
As discussed earlier, an EntityType represents a class in your domain model. An instance of an EntityType is often
referred to as an entity. If you are using the Entity Framework designer, an EntityType is represented on the design
surface as a box with various properties. Figure 1-3 shows two EntityTypes: Employee and Task.
Chapter 1 ■ Getting Started with Entity Framework
5
An EntityType usually has one or more properties. Just like with a class, a property is a named value with a specific
data type. Properties can have simple types like integer, string, and so on; or have ComplexTypes; or be collections.
Navigation properties refer to other related entities (typically represented by foreign key relationships in a database).
The non-navigation properties on an EntityType are usually just called scalar properties.
A relationship between two entities is called an association. Associations between EntityTypes are shown on the
design surface as a line connecting the EntityTypes. The line is annotated to show the multiplicity on each end of the
association. The association in Figure 1-3 is a one-to-many association between Employee and Task. An Employee
can have zero or more tasks. Each Task is associated to exactly one Employee.
Every EntityType has a property or set of properties that denote its EntityKey. An EntityKey uniquely identifies
the entity to Entity Framework and is most often mapped to a primary key from the entity’s representation in the
underlying database.
Finally, no discussion on Entity Framework would be complete without mentioning the context object. The
context object for Entity Framework is your gateway into the Entity Framework services. The context object exposes
entity objects, manages the database connection, generates parameterized SQL, marshals data to and from the
database, caches objects, helps maintain change tracking and materializes, or transforms, an untyped result set into a
collection of strongly typed objects.
In the beginning, there was the ObjectContext object. Now, Entity Framework supports an alternate, more
streamlined context object called the DbContext. The DbContext greatly simplifies the developer experience when
working with Entity Framework. Interestingly, the DbContext is a wrapper, or facade, around the ObjectContext,
exposing the underlying ObjectContext functionality in an intuitive, friendly and productive way.
Clearly, the DbContext is the preferred approach for working with Entity Framework as we will demonstrate in
great detail in this book.
The Code
Despite a tremendous emphasis on visual design support, the Entity Framework is all about code. The models,
EntityTypes, associations, mappings, and so on are ultimately expressed in concrete code that becomes part of
your application. This code is either generated by Visual Studio and Entity Framework or created manually by the
development team. You can choose quite a bit about the code-generation process or the lack of it by changing various
properties on your project or modifying the underlying code-generation templates.
Visual Studio uses a code-generation technology called Text Template Transformation Toolkit, simply referred
to as T4 templates. The Visual Studio tooling uses T4 templates to generate, or scaffold, code automatically. The great
thing about T4 template support in Visual Studio is that you can edit the templates to tailor the code-generation
process to match your exact needs. This is an advanced technique, but it is necessary in some cases. We’ll show you
how to do this in a few recipes.
Figure 1-3. A model with Employee and Task with a one-to-many association between them
Chapter 1 ■ Getting Started with Entity Framework
6
Alternatively, you can leverage the more recent Code-First approach to manually create the concrete code
yourself, gaining direct control over the entire process. With Code First, the developer can create entity classes,
mappings and context object, all without the help of a designer. These manually created entity classes, commonly
referred to as POCO, or Plain Old CLR Objects, have no dependence on Entity Framework plumbing. Even more
interesting, the development team can leverage the Entity Framework Power Tool utilities (free download from
Microsoft) to reverse-engineer a Code First model from an existing database, foregoing the effort to have manually
create the entity classes, mappings and context object. The recipes in Chapter 8 show you the basics of creating and
using POCO. Many of the recipes throughout the book will show you how to use Code First across specific contexts
such as in n-Tier applications.
Visual Studio
Of course, the main tool we use when developing applications for the Windows environment is Visual Studio. This
Integrated Development Environment has evolved over many years from a simple C++ compiler and editor to a
highly integrated, multi-language environment that supports the entire software development lifecycle. Visual Studio
and its related tools and services provide for design, development, unit testing, debugging, software configuration
management, build management and continuous integration, and much more. Don’t be worried if you haven’t used
all these in your work; few developers have. The point is that Visual Studio is a full-featured toolset. Visual Studio
plays a vital role in the development of Entity Framework applications.
Visual Studio provides an integrated design surface for Entity Framework models. Using this design surface and
other tools in Visual Studio, you can create models from scratch or create them from an existing database. You also
have the option to completely eliminate the designer and manually craft your Entity Types and configuration.
If you have an existing database, which is the case for many of us with existing applications, Visual Studio
provides tools for importing your tables and relationships into a model. This fits nicely with the real world because
few of us have the luxury of developing brand-new applications. Most of us have to extend, maintain, and evolve our
existing code and databases.
Alternately, you can create a model from scratch by starting with an empty design surface and adding new
EntityTypes to the surface, creating both associations and inheritance hierarchies for your model. When you are done
creating the model, right-click the design surface and select Generate Database from Model.
If your project team is code-centric, you can instead create a set of domain classes, including relationships and a
context class and then wire up these classes to hook into the Entity Framework engine and features without having to
use a designer.
Once you have created your model, changes often happen. That’s the nature of software development. Visual
Studio provides tools for updating the model from the database. This will keep the model synchronized with changes
in the database. Additionally, the Entity Framework Team also supports a tool called Code First Migrations, which can
be used to keep your database up-to-date with changes in your model.
1-2. Using Entity Framework
Entity Framework is tightly integrated with Visual Studio. To implement Entity Framework in your application, add a
new ADO.NET Entity Data Model in your project. Right-click your project and select Add ➤ New Item. In the dialog
box (see Figure 1-4), choose the ADO.NET Entity Data Model template. This template is located under the Data
templates. Click Add to launch the Entity Data Model Wizard.
Chapter 1 ■ Getting Started with Entity Framework
7
There are two options on the first page of the Entity Data Model Wizard: start with an existing database or start
with an empty model. (The former option is actually labeled “Generate from database.”) This first page is shown in
Figure 1-5.
Figure 1-4. Adding a new model to your project
Chapter 1 ■ Getting Started with Entity Framework
8
Generating a model from an existing database is the Database-First approach. From the tables, views, and stored
procedures that you select from the underlying database, the wizard will create a model and entity classes, against
which you can write code. The immediate benefit here is that you write code against strongly typed entity classes,
which Entity Framework maps to the underlying database tables and columns. If the tables you include are related in
the database, these relationships will be modeled as associations. This is one way to create your model if you already
have a database for your application. However, if you prefer to use the Code-First approach with an existing database,
worry not. The Entity Framework team has created tooling (The Entity Framework Power Tools) that reverse-engineers
an existing database into domain entity classes, just as if you coded them by hand.
If you’re working on a brand-new application, without an existing database, you have options as well. In the
Entity Framework designer, you can start with an empty design surface. Right-click the design surface to create new
EntityTypes, associations, or inheritances. You can also drag them from the Toolbox onto the design surface. Once
your model is complete, just right-click the design surface and select Generate Database from Model. This will
generate a script you can use to create the database tables and relationships for the model.
Figure 1-5. The Entity Data Model Wizard gives you a choice between creating a model from an existing database or
starting with an empty model
Chapter 1 ■ Getting Started with Entity Framework
9
Alternately, you can manually create each of your entity classes in Visual Studio and simply register them in
the DbContext object, then hook into the Entity Framework services. Entity Framework will map the classes to the
underlying databases and automatically create a model in memory at runtime.
With the Model-First or Database-First approaches, you use the Entity Framework designer to develop your
model. The key parts of a model in the designer are shown in Figure 1-6. In this model, a Customer has a one-to-many
association with an Order. Each customer may have many orders, but each order is associated with just one customer.
The Mapping Details window shows that the Customer EntityType maps to the Customer table in the database.
The Mapping Detail window also shows the mapping between the columns in the Customer table and the scalar
properties in the Customer EntityType. Keep in mind that you can make the same kind of mapping configurations
using the data annotations or fluent API features found in the Code First approach to using Entity Framework.
Figure 1-6. Key parts of a model in the designer
Chapter 1 ■ Getting Started with Entity Framework
10
Of course, there’s more to the designer and model than just the few key parts illustrated in Figure 1-6. In the
recipes in this book, we’ll cover just about every aspect of using the designer to create models. In some cases, we go
beyond what can be done with the designer and show you how to create models that require direct editing of the
underlying .edmx file. The .edmx file contains the complete model definition, including the conceptual layer, store
layer, and mapping layer.
So, whether we implement Entity Framework with the Database-First, Model-First or Code-First approach,
we always end up with a model. We gain significant productivity, as we can program against objects in the model
(EntityTypes) as you do with other objects in your application. For the model in Figure 1-6, your code uses Customer
and Order in much the same way as you use other objects.
If you want to insert a new customer and order into the database, you can create instances of the Customer
and Order types, set the properties, add them to the in-memory context that represents the model, and call
SaveChanges(). All the necessary SQL code is generated and sent to the database to insert the rows. To retrieve
customers and orders from the database, you use either LINQ or Entity SQL to create a query in terms of the
EntityTypes and associations in the model.
The recipes throughout this book will show you step by step how to model just about every conceivable database
scenario; how to query, insert, update, and delete using these models; and how to use Entity Framework in many
kinds of applications.
11
Chapter 2
Entity Data Modeling Fundamentals
More likely than not, you are just beginning to explore Entity Framework, and you are probably asking the question,
“Okay, how do I get started?” If this describes you, this chapter is a great place to start. If, on the other hand,
you have built some working models and feel comfortable with a few key modeling concepts, such as entity splitting
and inheritance, you can skip this chapter.
In this chapter, we will walk you through the basic examples of modeling with Entity Framework. Modeling is
the core feature of Entity Framework and what distinguishes Entity Framework from previous Microsoft data access
platforms. Once you have built your model, you can write code against the model rather than against the rows and
columns in the relational database.
We start off this chapter with an example of how to create a simple conceptual model, and then let Entity
Framework create the underlying database. In the remaining examples, we will show you how to create models from
existing tables and relationships in your databases.
2-1. Creating a Simple Model
Problem
You have a brand new project, and you want to create a model.
Solution
Let’s imagine that you want to create an application to hold the names and phone numbers of people that you know.
To keep things simple, let’s assume that you need just one entity type: Person.
To create the new model, do the following:
1. Right-click your project, and select Add ➤ New Item.
2. From the templates, select ADO.NET Entity Data Model and click Add. This template is
located in Data under Visual C# Items (see Figure 2-1).
Chapter 2 ■ Entity Data Modeling Fundamentals
12
3. In the first step of the wizard, choose Empty Model and click Finish. The wizard will create
a new conceptual model with an empty design surface.
4. Right-click the design surface, and select Add ➤ Entity.
5. Type Person in the Entity name field, and select the box to Create a key property. Use Id
as the Key Property. Make sure that its Property Type is Int32. Click OK, and a new Person
entity will appear on the design surface (see Figure 2-2).
Figure 2-1. Adding a new .emdx file that contains XML describing the conceptual model, storage model,
and mapping layer
Chapter 2 ■ Entity Data Modeling Fundamentals
13
6. Right-click near the top of the Person entity, and select Add ➤ Scalar Property.
A new scalar property will be added to the Person entity.
7. Rename the scalar property FirstName. Add scalar properties for LastName, MiddleName,
and PhoneNumber.
8. Right-click the Id property, and select Properties. In the properties view, change
the StoreGeneratedPattern property to Identity if it is not already set to Identity.
This flags the Id property as a value that will be computed by the store layer (database).
The database script we get at the end will flag the Id column as an identity column,
and the storage model will know that the database will automatically manage the values
in this column.
The completed conceptual model should look like the model in Figure 2-3.
Figure 2-2. Adding a new entity type representing a Person in our conceptual model
Chapter 2 ■ Entity Data Modeling Fundamentals
14
You now have a simple conceptual model. To generate a database for our model, there are a few things we still
have to do:
9. We need to change a couple of properties of our model to help with housekeeping.
Right-click the design surface, and select properties. Change the Database Schema Name
to Chapter2, and change the Entity Container Name to EF6RecipesContext. Figure 2-4
illustrates these changes.
Figure 2-4. Changing the properties of our model
Figure 2-3. Our completed model with an entity type representing a Person
Chapter 2 ■ Entity Data Modeling Fundamentals
15
10. Right-click the design surface, and select Generate Database Script from Model. Select an
existing database connection or create a new one. In Figure 2-5, we’ve opted to create a
new connection to our local machine and to the database EF6Recipes.
11. Click OK to complete the connection properties, and click Next to preview the database
script (see Figure 2-6). Once you click Finish, the generated script is added to your project.
Figure 2-5. Creating a new database connection that will be used by Entity Framework to create a database script that
we can use to create a database from our conceptual model
Chapter 2 ■ Entity Data Modeling Fundamentals
16
12. Run the database script in an SSMS query window to create the database and the
People table.
How It Works
The Entity Framework Designer is a powerful tool for creating and updating a conceptual model, storage model,
and mapping layer. This tool provides support for bidirectional model development. You can either start with a clean
design surface and create a model; or start with a database that you already have and import it to create a conceptual
model, storage model, and mapping layer. The current version of the Designer supports somewhat limited roundtrip
modeling, allowing you to re-create your database from a model and update the model from changes in your
database.
The model has a number of properties that affect what goes in the generated storage model and database script.
We changed two of these properties. The first was the name of the container. This is the class derived from DbContext.
We called this EF6RecipesContext to be consistent with the contexts we use throughout this book.
Additionally, we changed the schema to “Chapter 2.” This represents the schema used to generate the storage
model as well as the database script.
Figure 2-6. Generating the storage model in the .edmx file and creating the database script
Chapter 2 ■ Entity Data Modeling Fundamentals
17
The code in Listing 2-1 demonstrates one simple way to create and insert instances of our Person entity type.
The code also demonstrates iterating through all the Person entities in our database.
Listing 2-1. Inserting into and Retrieving from Our Model
using (var context = new EF6RecipesContext())
{
var person = new Person { FirstName = Robert, MiddleName=Allen,
LastName = Doe, PhoneNumber = 867-5309 };
context.People.Add(person);
person = new Person { FirstName = John, MiddleName=K.,
LastName = Smith, PhoneNumber = 824-3031 };
context.People.Add(person);
person = new Person { FirstName = Billy, MiddleName=Albert,
LastName = Minor, PhoneNumber = 907-2212 };
context.People.Add(person);
person = new Person { FirstName = Kathy, MiddleName=Anne,
LastName = Ryan, PhoneNumber = 722-0038 };
context.People.Add(person);
context.SaveChanges();
}
using (var context = new EF6RecipesContext())
{
foreach (var person in context.People)
{
System.Console.WriteLine({0} {1} {2}, Phone: {3},
person.FirstName, person.MiddleName,
person.LastName, person.PhoneNumber);
}
}
The output of the code in Listing 2-1 should look something like the following:
John K. Smith, Phone: 824-3031
Robert Allen Doe, Phone: 867-5309
Kathy Anne Ryan, Phone: 722-0038
Billy Albert Minor, Phone: 907-2212
Best Practice
When we created a new instance of the database context, we did it within a using() statement:
using (var context = new EF6RecipesContext())
{
...
}
Chapter 2 ■ Entity Data Modeling Fundamentals
18
If you are not familiar with this pattern, it’s really pretty simple. Normally, when we get a new instance of an
object, we use the new operator and assign the result to some variable. When the variable goes out of scope and the
object is no longer referenced by anything else, the garbage collector will do its job at some point and reclaim the
memory for the object. That works great for most of the objects that we create in our .NET applications because most
objects hold on to resources that can wait around for whenever the garbage collector has a chance to reclaim them.
The garbage collector is rather nondeterministic. It reclaims resources pretty much on its own schedule, which we can
only partially influence.
Instances of DbContext hold on to system resources such as database connections that we want to release as
soon as we’re done with them. We don’t really want these database connections to stay open waiting for the garbage
collector eventually to reclaim them.
There are a few nice features of using() statements. First, when the code execution leaves the using() {} block,
the Dispose() method on the context will be called because DbContext implements the IDisposable interface. For
DbContext, the Dispose() method closes any active database connections and properly cleans up any other resources
that need to be released.
Second, no matter how the code leaves the using(){} block, the Dispose() method is called. Most importantly,
this includes return statements and exceptions that may be thrown within the code block. The using(){} block is kind
of a guarantee that critical resources will be reclaimed properly.
The best practice here is always to wrap your code in the using(){} block when creating new instances of
DbContext. It’s one more step to help bulletproof your code.
2-2. Creating a Model from an Existing Database
Problem
You have an existing database with tables, perhaps a few views, and some foreign key constraints, and you want to
create a model for this database.
Solution
Let’s say that you have database describing poets and their poetry. Your relational database might look something like
the diagram in Figure 2-7.
From this database diagram, you can see that a poet can be the author of one or more poems, and each poem
can be categorized by its meter, which is the basic pattern of a poem’s verse. It’s not shown in this diagram, but our
database also has a view that joins the tables together so that we can more easily enumerate each poet and poem,
as well as the poem’s meter.
Figure 2-7. A simple database for poets and their poetry
Chapter 2 ■ Entity Data Modeling Fundamentals
19
To import the view, tables, and relationships into a model, do the following:
1. Right-click your project, and select Add ➤ New Item.
2. From the Visual C# Items Data templates, select ADO.NET Entity Data Model.
3. Select Generate from database to create the model from our existing tables. Click Next.
4. Either choose an existing connection to your database or create a new connection.
If you are creating a new connection, you will need to select your database server,
your authentication method (Windows or SQL Server), and the database. Once you have
selected these, it’s a good idea to click Test Connection to be sure that the connection is
ready to go. Once you have tested the connection, click Next.
The next dialog box shows all of the tables, views, and stored procedures in the database.
Check the items you want to include in the model. We want to select all of the tables
(Meter, Poem, and Poet). We also want to select the view (vwLibrary). For now, leave
the two check boxes for pluralizing and including foreign key columns selected. We will
discuss them further momentarily. Figure 2-8 shows the things we’ve selected.
When you click Finish, the wizard will create a new model with our three tables and the view. The wizard will also
read the foreign key constraints from the database and infer a one-to-many relationship between Poet and Poem(s)
as well as a one-to-many relationship between Meter and Poem(s).
Figure 2-8. Selecting the tables and view to include in our model. Leave the Pluralize or singularize generated object
names and Include Foreign Key Columns in the model checked
Chapter 2 ■ Entity Data Modeling Fundamentals
20
Figure 2-9 shows the new model created for us by including the Poet, Poem, and Meter tables as well as the
vwLibrary view.
You now have a model that you can use in your code. Note that the vwLibrary entity is based on the vwLibrary
view in our database. In most databases, views are read-only objects: inserts, deletes, and updates are typically not
supported at the database layer. This is also the case with Entity Framework. Entity Framework considers views read
only. You can get around this by mapping stored procedures for the create, update, and delete actions for view-based
entities. We will show you how to do just that in Chapter 6.
How It Works
Let’s look at the model created for us by the importing process. Notice that the entities have scalar properties and
navigation properties. The scalar properties map to the columns in the tables of the database, while the navigation
properties are derived from the relationships between the tables.
In our database diagram, a poem has a meter and a poet (the author). These correspond to the Meter and Poet
navigation properties. If we have an instance of a Poem entity, the Poet navigation property holds an instance of a Poet
entity, while the Meter navigation property holds an instance of a Meter entity.
A poet can be the author of any number of poems. The Poems navigation property contains a collection of
instances of the Poem entity. This collection can be empty, of course, for those poets that have yet to write any poetry.
For the Meter entity, the Poems navigation property is also a collection. For this navigation property, the collection
holds instances of Poems that have the given meter. SQL Server does not support relationships defined on views, and
our model reflects this with an empty set of navigation properties on the vwLibrary entity.
Notice that the Import Wizard was smart enough to pluralize the navigation properties that contained collections.
If you right-click the entities and look at their properties, you will notice that the entity set names for each of the
Figure 2-9. Our completed model
Chapter 2 ■ Entity Data Modeling Fundamentals
21
entities are also property pluralized. For example, the entity set name for the Poem entity is Poems. This automatic
pluralization happened because we left the Pluralize or singularize generated object names option checked.
The Include Foreign Key Columns in the model option also caused the foreign keys to be included in the model.
Although it may seem a little unnecessary to have both foreign keys and navigation properties, we’ll see in many of the
following recipes that having direct access to the foreign keys can be useful.
The code in Listing 2-2 demonstrates how to create instances of Poet, Poem, and Meter entities in our model and
how to save these entities to our database. The code also shows you how to query the model to retrieve the poets and
poems from the database.
Listing 2-2. Inserting into and Querying Our Model
using (var context = new EF6RecipesContext())
{
var poet = new Poet { FirstName = John, LastName = Milton };
var poem = new Poem { Title = Paradise Lost };
var meter = new Meter { MeterName = Iambic Pentameter };
poem.Meter = meter;
poem.Poet = poet;
context.Poems.Add(poem);
poem = new Poem { Title = Paradise Regained };
poem.Meter = meter;
poem.Poet = poet;
context.Poems.Add(poem);
poet = new Poet { FirstName = Lewis, LastName = Carroll };
poem = new Poem { Title = The Hunting of the Shark };
meter = new Meter { MeterName = Anapestic Tetrameter };
poem.Meter = meter;
poem.Poet = poet;
context.Poems.Add(poem);
poet = new Poet { FirstName = Lord, LastName = Byron };
poem = new Poem { Title = Don Juan };
poem.Meter = meter;
poem.Poet = poet;
context.Poems.Add(poem);
context.SaveChanges();
}
using (var context = new EF6RecipesContext())
{
var poets = context.Poets;
foreach (var poet in poets)
{
Console.WriteLine({0} {1}, poet.FirstName, poet.LastName);
foreach (var poem in poet.Poems)
{
Console.WriteLine(t{0} ({1}), poem.Title, poem.Meter.MeterName);
}
}
}
Chapter 2 ■ Entity Data Modeling Fundamentals
22
// using our vwLibrary view
using (var context = new EF6RecipesContext())
{
var items = context.vwLibraries;
foreach (var item in items)
{
Console.WriteLine({0} {1}, item.FirstName, item.LastName);
Console.WriteLine(t{0} ({1}), item.Title, item.MeterName);
}
}
In the first block of code in Listing 2-2, we create instances of the Poet, Poem, and Meter entity types for the poet
John Milton, his poem “Paradise Lost,” and the meter for the poem, which in this case is Iambic Pentameter. Once we
have created the instances of the entity types, we set the poem’s Meter property to the meter instance and the poem’s
Poet property to the poet instance. Using the same approach, we build up the other entities relating each poem to its
meter and poet. Once we have everything in place, we call SaveChanges()to generate and execute the appropriate
SQL statements to insert the rows into the underlying database.
The output from the code in Listing 2-2 is as follows:
Lord Byron
Don Juan (Anapestic Tetrameter)
Lewis Carroll
The Hunting of the Shark (Anapestic Tetrameter)
John Milton
Paradise Regained (Iambic Pentameter)
Paradise Lost (Iambic Pentameter)
Lewis Carroll
The Hunting of the Shark (Anapestic Tetrameter)
Lord Byron
Don Juan (Anapestic Tetrameter)
John Milton
Paradise Regained (Iambic Pentameter)
John Milton
Paradise Lost (Iambic Pentameter)
In the code, we start by creating and initializing instances of the poet, poem, and meter for the first of John
Milton’s poems. Once we have these in place, we set the poem’s Meter navigation property and the poem’s Poet
navigation property to the instances of poem and meter. Now that we have the poem instance completed, we add it
using the Add() method. Entity Framework does all of the remaining work of adding the poem to the Poems collection
on the poet instance and adding the poem to the Poems collection on the meter instance. The rest of the setup follows
the same pattern. To shorten the code, we reuse variables and instances where we can.
Once we have all of the objects created and all the navigation properties initialized, we have completed the
object graph. Entity Framework keeps track of the changes we’ve made to build the object graph. These changes
are tracked in the database context. Our context variable contains an instance of the database context (it’s of type
DbContext), and it is what we used to build the object graph. To send these changes to the database, we call the
SaveChanges() method.
Chapter 2 ■ Entity Data Modeling Fundamentals
23
To query our model and, of course, verify that we did indeed save everything to the database, we grab a fresh
instance of the object context and query it using LINQ to Entities. We could have reused the same instance of the
database context, but then we know it has the object graph and any subsequent queries we run against it won’t flow
through to the database because the graph is already in memory.
Using LINQ to Entities, we query for all of the poets, and for each poet we print out the poet’s name and the
details for each of their poems. The code is pretty simple, but it does use a couple of nested for loops.
The last block of code uses the vwLibrary entity. This entity is based on our vwLibrary view. This view joins the
tables together to flatten things out a bit and provide a cleaner perspective. When we query for each poet against the
vwLibraries entity set, we can get by with just one for loop. The output is a little different because we repeat the poet’s
name for each poem.
There is one last thing to note in this example. We didn’t insert the poets, poems, and meters using the vwLibrary
entity because views are always read-only in most database systems. In Entity Framework, we can’t insert (or update,
or delete) entities that are based on views. Of course, we’ll show you exactly how to overcome this little challenge in
many of the recipes in this book!
2-3. Modeling a Many-to-Many Relationship with No Payload
Problem
You have a couple of tables in an existing database that are related to each other via a link or junction table. The link
table contains just the foreign keys used to link the two tables together into a many-to-many relationship. You want
to import these tables to model this many-to-many relationship.
Solution
Let’s say that your database tables look something like the database diagram in Figure 2-10.
To create a model and import these tables and relationships, do the following:
1. Add a new model to your project by right-clicking your project and selecting Add ➤ New
Item. Choose ADO.NET Entity Data Model from the Visual C# Items Data templates.
2. Select Generate from database. Click Next.
3. Use the wizard to select an existing connection to your database, or create a new
connection.
4. From the Choose Your Database Object dialog box, select the tables Album, LinkTable,
and Artist. Leave the Pluralize and Foreign Key options checked. Click Finish.
The wizard will create the model shown in Figure 2-11.
Figure 2-10. Artists and albums in a many-to-many relationship
Chapter 2 ■ Entity Data Modeling Fundamentals
24
The many-to-many relationship between Album and Artist is represented by a line with the * character on
both ends. Because an Album can have many Artists, and an Artist can responsible for many Albums, each of these
navigation properties is of type EntityCollection.
How It Works
In Figure 2-11, an artist can be related to many albums, whereas an album can be the work of many artists. Notice
that the link table from Figure 2-10 is not represented as an entity in our model. Because our link table has no scalar
properties (that is, it has no payload), Entity Framework assumes that its sole purpose is to create the association
between Album and Artist. If the link table had scalar properties, Entity Framework would have created a very
different model, as we will see in the next recipe.
The code in Listing 2-3 demonstrates how to insert new albums and artists into our model and how to query our
model for both artists and their albums and albums with their artists.
Listing 2-3. Inserting and Querying Our Artists and Albums Model Through the Many-to-Many Association
using (var context = new EF6RecipesContext())
{
// add an artist with two albums
var artist = new Artist { FirstName = Alan, LastName = Jackson };
var album1 = new Album { AlbumName = Drive };
var album2 = new Album { AlbumName = Live at Texas Stadium };
artist.Albums.Add(album1);
artist.Albums.Add(album2);
context.Artists.Add(artist);
// add an album for two artists
var artist1 = new Artist { FirstName = Tobby, LastName = Keith };
var artist2 = new Artist { FirstName = Merle, LastName = Haggard };
var album = new Album { AlbumName = Honkytonk University };
artist1.Albums.Add(album);
artist2.Albums.Add(album);
context.Albums.Add(album);
context.SaveChanges();
}
Figure 2-11. The model with a many-to-many relationship between our tables
Chapter 2 ■ Entity Data Modeling Fundamentals
25
using (var context = new EF6RecipesContext())
{
Console.WriteLine(Artists and their albums...);
var artists = context.Artists;
foreach (var artist in artists)
{
Console.WriteLine({0} {1}, artist.FirstName, artist.LastName);
foreach (var album in artist.Albums)
{
Console.WriteLine(t{0}, album.AlbumName);
}
}
Console.WriteLine(nAlbums and their artists...);
var albums = context.Albums;
foreach (var album in albums)
{
Console.WriteLine({0}, album.AlbumName);
foreach (var artist in album.Artists)
{
Console.WriteLine(t{0} {1}, artist.FirstName, artist.LastName);
}
}
}
The output from the code in Listing 2-3 looks like the following:
Artists and their albums...
Alan Jackson
Drive
Live at Texas Stadium
Tobby Keith
Honkytonk University
Merle Haggard
Honkytonk University
Albums and their artists...
Drive
Alan Jackson
Live at Texas Stadium
Alan Jackson
Honkytonk University
Tobby Keith
Merle Haggard
After getting an instance of our database context, we create and initialize an instance of an Artist entity type and
a couple of instances of the Album entity type. We add the albums to the artist and then add the artist to the Database
Context.
Next we create and initialize a couple of instances of the Artist entity type and an instance of the Album entity
type. Because the two artists collaborated on the album, we add the album to both artists’ Albums navigation property
(which is of type EntityCollection). Adding the album to the Database Context causes the artists to get added as well.
Chapter 2 ■ Entity Data Modeling Fundamentals
26
Now that the completed object graph is part of the database context, the only thing left to do is to use
SaveChanges() to save the whole thing to the database.
When we query the database in a brand-new Database Context, we grab the artists and display their albums.
Then we grab the albums and print the artists that created the albums.
Notice that we never refer to the underlying LinkTable from Figure 2-10. In fact, this table is not even represented
in our model as an entity. The LinkTable is represented in the many-to-many association, which we access via the
Artists and Albums navigation properties.
2-4. Modeling a Many-to-Many Relationship with a Payload
Problem
You have a many-to-many relationship in which the link table contains some payload data (any additional columns
beyond the foreign keys), and you want to create a model that represents the many-to-many relationship as two
one-to-many associations.
Solution
Entity Framework does not support associations with properties, so creating a model like the one in the previous
recipe won’t work. As we saw in the previous recipe, if the link table in a many-to-many relationship contains just the
foreign keys for the relationship, Entity Framework will surface the link table as an association and not as an entity
type. If the link table contains additional information, Entity Framework will create a separate entity type to represent
the link table. The resulting model will contain two one-to-many associations with an entity type representing the
underlying link table.
Suppose we have the tables and relationships shown in Figure 2-12.
Figure 2-12. A many-to-many relationship with payload
An Order can have many Items. An Item can be on many Orders. Additionally, we have a Count property
connected to each instance of the Order, Item relationship. This Count property is referred to as a payload.
To create a model and import these tables and relationships into the model, do the following:
1. Add a new model to your project by right-clicking your project and selecting
Add ➤ New Item. Choose ADO.NET Entity Data Model from the Visual C# Data templates.
2. Select Generate from database. Click Next.
3. Use the wizard to select an existing connection to your database or create a new
connection.
4. From the Choose Your Database Object dialog box, select the tables Order, OrderItem,
and Item. Leave the Pluralize and Foreign Key options checked. Click Finish.
The wizard will create the model in Figure 2-13.
Chapter 2 ■ Entity Data Modeling Fundamentals
27
How It Works
As we saw in the previous recipe, for a many-to-many relationship with no payload, the model is clean and simple to
navigate. Because Entity Framework does not support the notion of payloads on associations, it surfaces the link table
as an entity with two one-to-many associations to the related entities. In this case, the OrderItem table is represented
not as an association, but as an entity type with a one-to-many association to Order and a one-to-many association
to Item. In the previous recipe, the payload-free link table did not translate into an entity type in the model. Instead,
it became part of the many-to-many association.
The addition of a payload requires an additional hop through the entity representing the link table to retrieve the
related items. This is illustrated in code in Listing 2-4.
Listing 2-4. Inserting into and Retrieving from the Model
using (var context = new EF6RecipesContext())
{
var order = new Order { OrderId = 1,
OrderDate = new DateTime(2010, 1, 18) };
var item = new Item { SKU = 1729, Description = Backpack,
Price = 29.97M };
var oi = new OrderItem { Order = order, Item = item, Count = 1 };
item = new Item { SKU = 2929, Description = Water Filter,
Price = 13.97M };
oi = new OrderItem { Order = order, Item = item, Count = 3 };
item = new Item { SKU = 1847, Description = Camp Stove,
Price = 43.99M };
oi = new OrderItem { Order = order, Item = item, Count = 1 };
context.Orders.Add(order);
context.SaveChanges();
}
using (var context = new EF6RecipesContext())
{ foreach (var order in context.Orders)
{
Console.WriteLine(Order # {0}, ordered on {1},
order.OrderId.ToString(),
order.OrderDate.ToShortDateString());
Figure 2-13. Two one-to-many associations from a many-to-many relationship with payload
Chapter 2 ■ Entity Data Modeling Fundamentals
28
Console.WriteLine(SKUtDescriptiontQtytPrice);
Console.WriteLine(---t-----------t---t-----);
foreach (var oi in order.OrderItems)
{
Console.WriteLine({0}t{1}t{2}t{3}, oi.Item.SKU,
oi.Item.Description, oi.Count.ToString(),
oi.Item.Price.ToString(C));
}
}
}
The following is the output from the code shown in Listing 2-4.
Order # 1, ordered on 1/18/2010
SKU Description Qty Price
---- ----------- --- ------
1729 Backpack 1 $29.97
1847 Camp Stove 1 $43.99
2929 Water Filter 3 $13.97
After we create an instance of our database context, we create and initialize an Order entity as well as the items
and order items for the order. We connect the order with the items by initializing the OrderItem entities with the
instances of the Order entity and the Item entity. We use the Add() method to add the order to the context.
With the object graph complete and the order added to the context, we update the database with the
SaveChanges() method.
To retrieve the entities from the database, we create a fresh instance of the context and iterate through the
context.Orders collection. For each order (well, we just have one in this example), we print the order detail and
we iterate through the entity collection on the OrderItems navigation property. These instances of the OrderItem
entity type give us access to the Count scalar property (the payload) directly, and each item on the order via the Item
navigation property. Going through the OrderItems entity to get to the items is the “extra” hop that is the cost of
having a payload in the link table (OrderItems, in our example) in a many-to-many relationship.
Best Practice
Unfortunately, a project that starts out with several payload-free many-to-many relationships often ends up with
several payload-rich many-to-many relationships. Refactoring a model, especially late in the development cycle,
to accommodate payloads in the many-to-many relationships can be tedious. Not only are additional entities
introduced, but the queries and navigation patterns through the relationships change as well. Some developers
argue that every many-to-many relationship should start off with some payload, typically a synthetic key, so that the
inevitable addition of more payload has significantly less impact on the project.
So here’s the best practice: If you have a payload-free many-to-many relationship and you think there is some
chance that it may change over time to include a payload, start with an extra identity column in the link table. When
you import the tables into your model, you will get two one-to-many relationships, which means the code you write
and the model you have will be ready for any number of additional payload columns that come along as the project
matures. The cost of an additional integer identity column is usually a pretty small price to pay to keep the model
more flexible.
Chapter 2 ■ Entity Data Modeling Fundamentals
29
2-5. Modeling a Self-Referencing Relationship with
a Code-First Approach
Problem
You have a table that references itself, and you want to model this as an entity with a self-referencing association using
a Code-First approach.
Solution
Let’s say that you have a self-referencing table that’s like the one shown in the database diagram in Figure 2-14.
To create a model and import this table and the self-referencing relationship into the model, do the following:
1. Create a new class that inherits from DbContext in your project.
2. Use the code in Listing 2-5 to create the PictureCategory POCO entity.
Listing 2-5. Creating the PictureCategory POCO Entity
public class PictureCategory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CategoryId { get; private set; }
public string Name { get; set; }
public int? ParentCategoryId { get; private set; }
[ForeignKey(ParentCategoryId)]
public PictureCategory ParentCategory { get; set; }
public ListPictureCategory Subcategories { get; set; }
public PictureCategory()
{
Subcategories = new ListPictureCategory();
}
}
Figure 2-14. A self-referencing table
Chapter 2 ■ Entity Data Modeling Fundamentals
30
3. Add a DbSetPictureCategory auto property to your DbContext subclass.
4. Override the OnModelCreating method in your DbContext class to configure the
bidirectional association (ParentCategory and SubCategories), as seen in Listing 2-6.
Listing 2-6. Overriding OnModelCreating in DbContext Subclass
public class EF6RecipesContext : DbContext
{
public DbSetPictureCategory PictureCategories { get; set; }
public PictureContext() : base(name=EF6CodeFirstRecipesContext)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.EntityPictureCategory()
.HasMany(cat = cat.SubCategories)
.WithOptional(cat = cat.ParentCategory);
}
}
How It Works
Database relationships are characterized by degree, multiplicity, and direction. Degree is the number of entity types
that participate in the relationship. Unary and binary relationships are the most common. Tertiary and n-place
relationships are more theoretical than practical.
Multiplicity is the number of entity types on each end of the relationship. You have seen the multiplicities 0..1
(zero or 1), 1 (one), and * (many).
Finally, the direction is either one-way or bidirectional.
The Entity Data Model supports a particular kind of database relationship called an Association Type.
An Association Type relationship has either unary or binary degree, multiplicities 0..1, 1, or *, and a direction that
is bidirectional.
In this example, the degree is unary (just the entity type PictureCategory is involved), the multiplicity is 0..1
and *, and the direction is, of course, bidirectional.
As is the case in this example, a self-referencing table often denotes a parent-child relationship, with each parent
having many children while each child has just one parent. Because the parent end of the relationship is 0..1 and
not 1, it is possible for a child to have no parent. This is just what you want to leverage in representing the root node;
that is, the one node that has no parent and is the top of the hierarchy.
Listing 2-7 shows how you can recursively enumerate the picture categories starting with the root node, which,
of course, is the only node that has no parent.
Chapter 2 ■ Entity Data Modeling Fundamentals
31
Listing 2-7. Inserting into Our Model and Recursively Enumerating All of the Instances of the Self-referencing entity
static void RunExample()
{
using (var context = new EF6RecipesContext())
{
var louvre = new PictureCategory { Name = Louvre };
var child = new PictureCategory { Name = Egyptian Antiquites };
louvre.Subcategories.Add(child);
child = new PictureCategory { Name = Sculptures };
louvre.Subcategories.Add(child);
child = new PictureCategory { Name = Paintings };
louvre.Subcategories.Add(child);
var paris = new PictureCategory { Name = Paris };
paris.Subcategories.Add(louvre);
var vacation = new PictureCategory { Name = Summer Vacation };
vacation.Subcategories.Add(paris);
context.PictureCategories.Add(paris);
context.SaveChanges();
}
using (var context = new EF6RecipesContext())
{
var roots = context.PictureCategories.Where(c = c.ParentCategory == null);
roots.ForEach(root = Print(root, 0));
}
}
static void Print(PictureCategory cat, int level)
{
StringBuilder sb = new StringBuilder();
Console.WriteLine({0}{1}, sb.Append(' ', level).ToString(), cat.Name);
cat.Subcategories.ForEach(child = Print(child, level + 1));
}
The output of the code in Listing 2-7 shows our root node: Summer Vacation. The first (and only) child is Paris.
Paris has Louvre as a child. Finally, at the Louvre, we categorized our pictures by the various collections we visited.
Summer Vacation
Paris
Louvre
Egyptian Antiquities
Sculptures
Paintings
Clearly, the code is a little involved. We start by creating and initializing the instances of our entity types. We wire
them together in the object graph by adding the PictureCategories to our louvre category. Then we add the louvre
category to the paris category. Finally, we add the paris category to our summer vacation category. We build the
hierarchy from the bottom up.
Chapter 2 ■ Entity Data Modeling Fundamentals
32
Once we do a SaveChanges(), the inserts are all done on the database, and it’s time to query our tables to see
whether we’ve actually inserted all of the rows correctly.
For the retrieval part, we start by getting the root entity. This is the one that has no parent. In our case, we created
a summer vacation entity, but we didn’t make it the child of any other entity. This makes our summer vacation entity
the root of the hierarchy.
Now, with the root, we call another method we wrote: Print(). The Print() method takes a couple of
parameters. The first parameter is an instance of a PictureCategory. The second parameter is a level, or depth,
we are at in the hierarchy. With the root category, summer vacation, we’re at the top of the hierarchy, so we pass in 0.
The method call looks like Print(root, 0).
In the Print() method, we write out the name of the category preceded by a space for each level deep in the
hierarchy. One of the Append() methods of the StringBuilder class takes a character and an integer. It creates an
instance of StringBuilder with the character appended the number of times specified by the integer parameter. In our
call, we send in a space and level, and it returns a string with a space for every level deep that we are in the hierarchy.
We use the ToString() method to convert the StringBuilder instance to a string.
Now for the recursive part: We iterate through the children and call the Print() method on each child, making
sure to increment the level by one. When we run out of children, we simply return. The result is the output shown
previously.
In Recipe 6-5, we show another approach to this problem using a Common Table Expression in a stored
procedure on the store side to iterate through the graph and return a single flattened result set.
2-6. Splitting an Entity Among Multiple Tables
Problem
You have two or more tables that share the same primary key, and you want to map a single entity to these two tables.
Solution
Let’s illustrate the problem with the two tables shown in Figure 2-15.
Figure 2-15. Two tables, Product and ProductWebInfo, with common primary keys
To create a model with a single entity representing these two tables, do the following:
1. Create a new class in your project that inherits from DbContext.
2. Create a Product POCO entity using the code in Listing 2-8.
Chapter 2 ■ Entity Data Modeling Fundamentals
33
Listing 2-8. Creating the Product POCO Entity
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int SKU { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string ImageURL { get; set; }
}
3. Add an auto-property of type DbSetProduct to your DbContext subclass.
4. Override the OnModelCreating() method of DbContext with the code in Listing 2-9.
Listing 2-9. Overriding OnModelCreating in the DbContext Subclass
public class EF6RecipesContext : DbContext
{
public DbSetProduct Products { get; set; }
public ProductContext() : base(name=EF6CodeFirstRecipesContext)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.EntityProduct()
.Map(m =
{
m.Properties(p = new {p.SKU, p.Description, p.Price});
m.ToTable(Product, Chapter2);
})
.Map(m =
{
m.Properties(p = new {p.SKU, p.ImageURL});
m.ToTable(ProductWebInfo, Chapter2);
});
}
}
How It Works
It seems all too common in legacy systems to find “extra” information for each row in one table tucked away in
another table. Often this happens over time as a database evolves, and no one is willing to break existing code by
adding columns to some critical table. The answer is to “graft on” a new table to hold the additional columns.
By merging two or more tables into a single entity or, as it is usually perceived, splitting a single entity across two
or more tables, we can treat all of the parts as one logical entity. This process is often referred to as vertical splitting.
Chapter 2 ■ Entity Data Modeling Fundamentals
34
The downside of vertical splitting is that retrieving each instance of our entity now requires an additional join for
each additional table that makes up the entity type. This extra join is shown in Listing 2-10.
Listing 2-10. Additional Join Required by Vertical Splitting
SELECT
[Extent1].[SKU] AS [SKU],
[Extent2].[Description] AS [Description],
[Extent2].[Price] AS [Price],
[Extent1].[ImageURL] AS [ImageURL]
FROM [dbo].[ProductWebInfo] AS [Extent1]
INNER JOIN [dbo].[Product] AS [Extent2] ON [Extent1].[SKU] = [Extent2].[SKU]
Nothing special is required to insert into or retrieve from the Product entity. Listing 2-11 demonstrates working
with the vertically split Product entity type.
Listing 2-11. Inserting into and Retrieving from Our Model with the Product Entity Type
using (var context = new EF6RecipesContext())
{
var product = new Product { SKU = 147,
Description = Expandable Hydration Pack,
Price = 19.97M, ImageURL = /pack147.jpg };
context.Products.Add(product);
product = new Product { SKU = 178,
Description = Rugged Ranger Duffel Bag,
Price = 39.97M, ImageURL = /pack178.jpg };
context.Products.Add(product);
product = new Product { SKU = 186,
Description = Range Field Pack,
Price = 98.97M, ImageURL = /noimage.jp };
context.Products.Add(product);
product = new Product { SKU = 202,
Description = Small Deployment Back Pack,
Price = 29.97M, ImageURL = /pack202.jpg };
context.Products.Add(product);
context.SaveChanges();
}
using (var context = new EF6RecipesContext())
{
foreach (var p in context.Products)
{
Console.WriteLine({0} {1} {2} {3}, p.SKU, p.Description,
p.Price.ToString(C), p.ImageURL);
}
}
Chapter 2 ■ Entity Data Modeling Fundamentals
35
The code in Listing 2-7 produces the following results:
147 Expandable Hydration Pack $19.97 /pack147.jpg
178 Rugged Ranger Duffel Bag $39.97 /pack178.jpg
186 Range Field Pack $98.97 /noimage.jpg
202 Small Deployment Back Pack $29.97 /pack202.jpg
2-7. Splitting a Table Among Multiple Entities
Problem
You have a table with some frequently used fields and a few large, but rarely needed fields. For performance reasons,
you want to avoid needlessly loading these expensive fields on every query. You want to split the table across two or
more entities.
Solution
Let’s say that you have a table like the one shown in Figure 2-16, which holds information about photographs as well
as the bits for both the thumbnail and full-resolution image of the photograph.
Figure 2-16. A Photograph table with a field holding the binary large object (blob) representing the data for the image
To create an entity type that contains the reasonably low-cost and frequently used columns, as well as an entity
type containing the high-cost but rarely used HighResolutionBits column, do the following:
1. Create a new class in your project that inherits from DbContext.
2. Create a Photograph POCO entity class using the code in Listing 2-12.
Chapter 2 ■ Entity Data Modeling Fundamentals
36
Listing 2-12. Creating the Photograph POCO Entity
public class Photograph
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PhotoId { get; set; }
public string Title { get; set; }
public byte[] ThumbnailBits { get; set; }
[ForeignKey(PhotoId)]
public virtual PhotographFullImage PhotographFullImage { get; set; }
}
3. Create a PhotographFullImage POCO entity class using the code in Listing 2-13.
Listing 2-13. Creating the PhotographFullImage POCO Entity
public class PhotographFullImage
{
[Key]
public int PhotoId { get; set; }
public byte[] HighResolutionBits { get; set; }
[ForeignKey(PhotoId)]
public virtual Photograph Photograph { get; set; }
}
4. Add an auto-property of type DbSetPhotograph to your DbContext subclass.
5. Add another auto-property type of DbSetPhotographFullImage to your DbContext
subclass.
6. Override the OnModelCreating() method of the DbContext class, as shown in Listing 2-14.
Listing 2-14. Overriding the OnModelCreating Method of DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.EntityPhotograph()
.HasRequired(p = p.PhotographFullImage)
.WithRequiredPrincipal(p = p.Photograph);
modelBuilder.EntityPhotograph().ToTable(Photograph, Chapter2);
modelBuilder.EntityPhotographFullImage().ToTable(Photograph, Chapter2);
}
Chapter 2 ■ Entity Data Modeling Fundamentals
37
How It Works
Entity Framework does not directly support the notion of lazy loading of individual entity properties. To get the effect
of lazy loading expensive properties, we exploit Entity Framework’s support for lazy loading of associated entities.
We created a new entity type to hold the expensive full image property and created a one-to-one association between
our Photograph entity type and the new PhotographFullImage entity type. We added a referential constraint on the
conceptual layer that, much like a database referential constraint, tells Entity Framework that a PhotographFullImage
can’t exist without a Photograph.
Due to the referential constraint, there are a couple of things to note about our model. If we have a newly created
PhotographFullImage, an instance of Photograph must exist in the object context or the data source prior to calling
SaveChanges(). Also, if we delete a photograph, the associated PhotographFullImage is also deleted. This is just like
cascading deletes in database referential constraints.
The code in Listing 2-15 demonstrates inserting and retrieving from our model.
Listing 2-15. Inserting into and Lazy Loading Expensive Fields
byte[] thumbBits = new byte[100];
byte[] fullBits = new byte[2000];
using (var context = new EF6RecipesContext())
{
var photo = new Photograph { Title = My Dog,
ThumbnailBits = thumbBits };
var fullImage = new PhotographFullImage { HighResolutionBits = fullBits };
photo.PhotographFullImage = fullImage;
context.Photographs.Add(photo);
context.SaveChanges();
}
using (var context = new EF6RecipesContext())
{
foreach (var photo in context.Photographs)
{
Console.WriteLine(Photo: {0}, ThumbnailSize {1} bytes,
photo.Title, photo.ThumbnailBits.Length);
// explicitly load the expensive entity,
PhotographFullImagecontext.Entry(photo).Reference(p = p.PhotographFullImage).Load();
Console.WriteLine(Full Image Size: {0} bytes,
photo.PhotographFullImage.HighResolutionBits.Length);
}
}
The output from Listing 2-15 is as follows:
Photo: My Dog, Thumbnail Size: 100 bytes
Full Image Size: 2000 bytes
The code in Listing 2-15 creates and initializes instances of the Photograph and PhotographFullImage entities,
adds them to the object context, and calls SaveChanges().
Chapter 2 ■ Entity Data Modeling Fundamentals
38
On the query side, we retrieve each of the photographs from the database, print some information about the
photograph, and then explicitly load the associated PhotographFullImage entity. Notice that we did not change the
default context option to turn off lazy loading. This puts the burden on us to load related entities explicitly. This is
just what we want. We could have chosen not to load the associated instances of PhotographFullImage, and if we
were iterating through hundreds or thousands of photographs, this would have saved us an awful lot of cycles and
bandwidth.
2-8. Modeling Table per Type Inheritance
Problem
You have some tables that contain additional information about a common table, and you want to model this using
table per type inheritance.
Solution
Suppose that you have two tables that are closely related to a common table, as shown in Figure 2-17. The Business
table is on the 1 side of a 1:0..1 relationship with the eCommerce and the Retail tables. The key feature here is that the
eCommerce and Retail tables extend information about a business represented in the Business table.
The tables Retail and eCommerce are related to the Business table, which holds a few properties that we would
naturally associate with any business. To model table per type inheritance such that entities Retail and eCommerce
inherit from the Business base entity type, perform the following steps:
1. Create a new class in your project that inherits from DbContext.
2. Create a Business POCO entity class using the code in Listing 2-16.
Figure 2-17. Closely related tables ripe for inheritance
Chapter 2 ■ Entity Data Modeling Fundamentals
39
Listing 2-16. Creating the Business POCO Entity Class
[Table(Business, Schema = Chapter2)]
public class Business
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BusinessId { get; protected set; }
public string Name { get; set; }
public string LicenseNumber { get; set; }
}
3. Create an eCommerce POCO entity class that inherits from the Business class using the
code in Listing 2-17.
Listing 2-17. Creating the eCommerce POCO Entity Class
[Table(eCommerce, Schema = Chapter2)]
public class eCommerce : Business
{
public string URL { get; set; }
}
4. Create a Retail POCO entity class that inherits from the Business class using the code
in Listing 2-18.
Listing 2-18. Creating the Retail POCO Entity Class
[Table(Retail, Schema = Chapter2)]
public class Retail : Business
{
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZIPCode { get; set; }
}
5. Add an auto-property of type DbSetBusiness to your DbContext subclass.
How It Works
Both the Retail and the eCommerce tables are on the 0..1 side of a 1:0..1 relationship with the Business table.
This means that we could have a business with no additional information or a business with additional Retail or
eCommerce information. In object-oriented programming terms, we have a base type, Business, with two derived
types, Retail and eCommerce.
Because of the 1:0..1 relationship, we cannot have a row in the Retail or eCommerce tables without a
corresponding row in the Business table. In object-oriented terms, an instance of a derived type has the properties
of the base type. This concept of a derived type extending the properties of a base type is a key feature of inheritance.
In table per type (often abbreviated TPT) inheritance, each of the derived types is represented in separate tables.
Chapter 2 ■ Entity Data Modeling Fundamentals
40
Listing 2-19 demonstrates inserting and retrieving from our model.
Listing 2-19. Inserting and Retrieving Entities in TPT Inheritance
using (var context = new EF6RecipesContext())
{
var business = new Business { Name = Corner Dry Cleaning,
LicenseNumber = 100x1 };
context.Businesses.Add(business);
var retail = new Retail { Name = Shop and Save, LicenseNumber = 200C,
Address = 101 Main, City = Anytown,
State = TX, ZIPCode = 76106 };
context.Businesses.Add(retail);
var web = new eCommerce { Name = BuyNow.com, LicenseNumber = 300AB,
URL = www.buynow.com };
context.Businesses.Add(web);
context.SaveChanges();
}
using (var context = new EF6RecipesContext())
{
Console.WriteLine(n--- All Businesses ---);
foreach (var b in context.Businesses)
{
Console.WriteLine({0} (#{1}), b.Name, b.LicenseNumber);
}
Console.WriteLine(n--- Retail Businesses ---);
foreach (var r in context.Businesses.OfTypeRetail())
{
Console.WriteLine({0} (#{1}), r.Name, r.LicenseNumber);
Console.WriteLine({0}, r.Address);
Console.WriteLine({0}, {1} {2}, r.City, r.State, r.ZIPCode);
}
Console.WriteLine(n--- eCommerce Businesses ---);
foreach (var e in context.Businesses.OfTypeeCommerce())
{
Console.WriteLine({0} (#{1}), e.Name, e.LicenseNumber);
Console.WriteLine(Online address is: {0}, e.URL);
}
}
The code in Listing 2-19 creates and initializes instances of the Business entity type and the two derived types.
To add these to the Database Context, we use the Add() method exposed on the Business entity set in the context.
On the query side, to access all of the businesses, we iterate through the Businesses entity set. For the derived
types, we use the OfType() method specifying the derived type to filter the Business entity set.
Chapter 2 ■ Entity Data Modeling Fundamentals
41
The output of Listing 2-19 looks like the following:
--- All Businesses ---
Corner Dry Cleaning (#100X1)
Shop and Save (#200C)
BuyNow.com (#300AB)
--- Retail Businesses ---
Shop and Save (#200C)
101 Main
Anytown, TX 76106
---- eCommerce Businesses ---
BuyNow.com (#300AB)
Online address is: www.buynow.com
Table per type is one of three inheritance models supported by Entity Framework. The other two are Table per
Hierarchy (discussed in this chapter) and Table per Concrete Type (see Chapter 15).
Table per type inheritance provides a lot of database flexibility because we can easily add tables as new derived
types find their way into our model as an application develops. However, each derived type involves additional joins
that can reduce performance. In real-world applications, we have seen significant performance problems with TPT
when many derived types are modeled.
Table per hierarchy, as you will see in Recipe 2-10, stores the entire hierarchy in a single table. This eliminates
the joins of TPT and thereby provides better performance, but at the cost of some flexibility.
Table per concrete type is supported by the Entity Framework runtime, but not by the designer. Table per
Concrete Type has some important applications, as we will see in Chapter 15.
2-9. Using Conditions to Filter an ObjectSet
Problem
You want to create a permanent filter on an entity type so that it maps to a subset of the rows in a table.
Solution
Let’s say that you have a table holding account information, as shown in the database diagram in Figure 2-18.
The table has a DeletedOn nullable column that holds the date and time the account was deleted. If the account
is still active, the DeletedOn column is null. We want our Account entity to represent only active accounts; that is,
an account without a DeletedOn value.
Chapter 2 ■ Entity Data Modeling Fundamentals
42
To model this table so that only active accounts are used to populate the Account entity type, do the following:
1. Add a new model to your project by right-clicking your project and selecting Add ➤ New
Item. Choose ADO.NET Entity Data Model from the Visual C# Data templates.
2. Select Generate from database. Click Next.
3. Use the wizard to select an existing connection to your database, or create a new
connection.
4. From the Choose Your Database Object dialog box, select the Account table. Leave the
Pluralize and Foreign Key options checked. Click Finish.
5. Click the Account entity to view the Mapping Details window. If the Mapping Details
window is not visible, show it by selecting View ➤ Other Windows ➤ Entity Data Model
Mapping Details. Click Add a Condition, and select the DeletedOn column. In the
Operator column, select Is, and in the Value/Property column, select Null. This creates a
mapping condition when the DeletedOn column is Is Null (see Figure 2-19).
Figure 2-18. Account table with DeletedOn DateTime column
Other documents randomly have
different content
to know these relations of his a little better. Hubert was a good chap,
and so was Uncle Joe. He had not properly understood them until to-
day. And now that he knew her story, he would like to know
something more of Eleanor. There was something fine about her, and
the thought of that dark, solemn little chit in the hall made him feel
oddly tender towards her.
The darkness had fallen, and the clouds had reassembled in
tremendous masses that were moving with strange swiftness across
the sky. Leaning back and looking upward it was interesting to
contrast the windless quiet of the garden in which they were sitting
with the evidences of the tumult above.
It's beginning to rain, his uncle suddenly exclaimed, breaking a
long silence. We'd better go.
Arthur was prepared for some display of temper on the part of Miss
Kenyon when he and his uncle entered the drawing-room, and was
disappointed to find that she displayed her habitual air of cold
reserve. He was a trifle nervous and apprehensive now, about this
projected embassy of his, and would have been glad to have been
stiffened by some show of active opposition. Miss Kenyon had, he
thought, something of the same awful detachment that her father
exhibited towards every-day affairs. All the older members of the
party were there. Turner had a novel in his hand, the three women
were busy with their usual fancy-work, but to-night they had drawn
together in a group by one of the windows, with an effect of being in
conference.
Joe Kenyon's action in pulling up a chair and joining the group held a
faint suggestion of bravado. He had the uneasy air of a man coming
to a confession of his own weakness.
Arthur preferred to stand, leaning against the jamb of the window. It
gave him a physical sense of superiority to look down upon his
antagonist.
Joe Kenyon plunged at once into what Arthur judged to be relatively
a side issue. Arthur and I have been talking about Hubert's
engagement, he said. Hubert had been telling him all about it this
afternoon; and Arthur has suggested that he should say something
to my father.
If he had deliberately intended an effect of surprise he had attained
his object. They were undoubtedly startled by this announcement,
and not less obviously puzzled. It was not, however, Arthur's part in
the affair that seemed to perplex them. None of them looked up at
him, they were all staring at Joe Kenyon, with an expression that
seemed, Arthur thought, to be seeking for a private sign. But so far
as he could see, none was given. Joe Kenyon was leaning back in his
chair and wiping his forehead. This rain ought to cool the air a bit,
he interjected in an undertone. Beastly hot in here.
Very friendly of Arthur, Turner commented, turning slightly towards
the young man as he spoke. No reason, after all, why he should
bother himself about our affairs.
I suppose he understands ... his wife began, and then stopped
abruptly. She was still looking anxiously at her brother as if inviting
further confidences.
Joe Kenyon nodded. Oh, of course, of course, he said. Hubert told
him all about it this afternoon.
About what, Joe? Miss Kenyon put in, speaking for the first time.
She gave him no indication of perturbation or anxiousness, but she
was reading her brother's face as if she sought some evidence of his
secret motive.
Well, about the engagement, and having no money and so on, Joe
Kenyon rather desperately explained.
No money? his sister returned, with a lift of her eyebrows. What
do you mean, by having no money?
Well, Hubert hasn't any, not of his own, her brother replied. And
he was saying, I gather, that he would like—well—a change of air if
he were married. About enough of us here, without him, perhaps.
That sort of thing. And Arthur very generously offered through me to
lend him a couple of hundred pounds if he wanted it.
Whether or not he had intended to create a diversion by this further
announcement, he had certainly achieved that object.
Turner gave an exclamation of surprise, but it was Mrs Kenyon who
answered.
Oh, but we couldn't possibly accept that, in an agitated voice; and
Arthur, looking down, saw that her hands were trembling. She was,
he realised then, by far the most nervous of the five, and he
recognised in her at that moment a strong likeness to his own
mother. She, too, had been a timid woman, apprehensive not only of
danger, but also of change. Miss Kenyon had let her work fall in her
lap, and was sitting, plunged, apparently, in a fit of deep abstraction.
No, no, of course not, Joe Kenyon replied. I have already refused
that.
On what grounds? Miss Kenyon put in sharply.
Er—I don't think—I suggested, Esther, that Hubert would be—well,
rather lost if he were to find himself in a new country with a wife to
support on a capital of £200.
Miss Kenyon gave a short impatient sniff, and turned to Arthur. A
little strange, isn't it, she asked, for you to offer to finance us?
Only Hubert, you know, Arthur explained.
Hubert has a father and mother alive, to say nothing of uncles and
aunts, she returned. I don't know why he should need help from a
comparative stranger.
He seemed to need it, Arthur said dryly, or I shouldn't have made
the offer.
Miss Kenyon shrugged her shoulders and turned back to her brother.
Are we to understand, Joe, she said, that Arthur Woodroffe knows
all about us now? Have you told him everything?
Damn it, Esther, what do you mean by everything? Joe Kenyon
exploded defensively. I—it seems to me—Hubert had pretty well told
him all that mattered, before I said a word. I told him about Jim, if
that's what you mean?
Miss Kenyon began to drum her fingers on the arm of her chair. And
what good do you expect to do to yourself or anybody else by
speaking to my father about Hubert's engagement? she asked
Arthur.
Turner leant back in his chair and crossed his legs. Precisely, that's
the real point, he agreed.
Well, naturally, I hope to persuade Mr Kenyon to sanction the
engagement, Arthur said.
Why? snapped Miss Kenyon.
Friendship for Hubert, Arthur said.
I wasn't aware that you and he were such great friends, was Miss
Kenyon's criticism of that explanation.
Oh, well, pretty fair, Arthur compromised. Anyhow, I'll be glad to
help him if I can.
I can't imagine that anything you could say to my father would carry
the least weight, Miss Kenyon said dryly.
Perhaps not, Arthur agreed. No harm in trying, though, is there?
I think that's quite true, you know, Esther, Mrs Kenyon put in, and
it would be rather a relief if—that is, I hope, for Hubert's sake at all
events, something can be done to smooth things over.
Miss Kenyon turned from her sister-in-law with a slight suggestion of
contempt. Do you know this girl, Dorothy Martin? she asked,
looking at her brother.
Slightly, he said. Met her twice, I think. Seemed a jolly girl, I
thought. Full of life.
Quite a nice girl, his wife put in eagerly.
Oh! you've met her too, have you? Miss Kenyon commented coldly.
At the Club House. Hubert took me up there to tea, the day before
yesterday, on purpose to introduce me, Mrs Kenyon explained, with
a pathetic air of apology.
Arthur had drawn many false inferences about the affairs at Hartling,
but it was quite clear to him now that although there might, as his
uncle had said, be some tacit agreement as to the Kenyons' attitude
toward the head of the house, Miss Kenyon had certainly not been
given any confidences concerning Hubert's engagement.
She has no money of her own, I suppose? was the next question.
Joe Kenyon and his wife looked at each other rather helplessly, and it
seemed that no further answer was needed, for Miss Kenyon at once
continued, Folly, absurd folly, and you know it. If Arthur Woodroffe
likes to make a fool of himself, he can. What he does or does not do
is neither here nor there. But I shall have no hand in it, and any
influence I have with my father....
She had risen to her feet as she spoke, and now stood with her
hands clenched, an erect and dominating figure. She was over sixty,
but she was still a handsome woman, full of vitality and energy; and
at that moment Arthur could not but concede her a grudging
measure of admiration. He felt as if he had seen her fully awake for
the first time. Her rather pale blue eyes were suddenly keen and
alert, and there was an air of mastery about her that reminded him
of her father. By the side of her, Mrs Turner and her brother with
their sandy-gray hair and their tendency to an untidy corpulence,
seemed to belong to another race. Esther, if the head of the house
was to be taken as the standard, was the only true Kenyon of the
second generation, unless Eleanor's father, the errant, independent
James, had been of his sister's breed? Had he, perhaps, had his
sister's hands also; those white, strong managing hands that were
now so threateningly clenched?
She stood there for a moment, dominating them all, while she
allowed the threat of her unfinished sentence to take effect; then she
turned and left the room with a quiet dignity that was in itself a
menace.
Nevertheless, Arthur at least had not been intimidated by her
outburst, and her contemptuous reference to himself had provided
him with the very stimulant he desired. Moreover, he had now a
fierce desire to humiliate his handsome opponent, a desire that arose
from a new source. He had seen her as a woman for the first time,
and he was aware in himself of a hitherto unrealised impulse to
cruelty. He wanted to break and dominate that proud, erect figure.
However sneeringly she had challenged him, and in the zest of his
unsatisfied youth, he longed to conquer her, although his victory
could be but the barren victory of the intellect.
He took the seat Miss Kenyon had just vacated with a pleasant sense
of mastery. He felt that he could do anything he liked with the other
four. They were all of them looking, just then, so completely cowed
and depressed. Joe Kenyon and his sister were crumpled into their
chairs, with an air of rather absurd dejection. Mrs Kenyon had
resumed her fancy work and was bending over it in an attitude that
suggested the possibility of hidden tears; and Turner, nervously
twisting his exquisitely neat little moustache, was staring thoughtfully
at his own reflection in the darkened window.
I don't see why we shouldn't help Hubert, all the same, Arthur
tried, by way of making a beginning.
Little Turner withdrew his gaze from the window and regarded the
intrepid youth with an expression of half-amused pity.
You don't know, was his only comment.
Well, I think I do, to a certain extent, Arthur said boldly. Uncle Joe
told me a good many things to-night, one way and another. More
than he cared to admit, perhaps, before Miss Kenyon.
He had made a deliberate bid for inclusion into their secret counsels
by that last sentence, and he had at least succeeded in stimulating
their interest.
Oh, well, well, his uncle said, sitting up with an effect of reinflation,
perhaps I did. Esther's got a queer temper, now and then. And
possibly I told you more than was altogether discreet. He looked at
his brother-in-law as he added, I'll admit to being a bit down in the
mouth about the whole affair.
But do you really think, Mrs Kenyon began unhopefully, that it
would be any good for you to come into the affair at all?
Well, I'm perfectly free, you know, Arthur said, and instantly
realised that he had said the forbidden thing. They could not bear
that admission of bondage in a full company.
Can't see that that's anything to do with it, Turner replied. We're
all free enough, so far as that goes. Point is, whether your
interference is advisable; whether you might not put Mr Kenyon's
back up and make things a hundred times worse for Hubert.
Arthur chose to overlook the snub. Well, I don't see that it could do
any harm, he said. He felt pleasantly young and capable among
those four old people; he believed that they were too inert to oppose
him, that they would accept any leader capable of taking the
initiative. Anything I did, he continued, would only react on me,
and I—don't care. Uncle Joe has warned me that Mr Kenyon may
sling me out of the house at an hour's notice, but I'm perfectly
willing to take that risk.
No one answered him. For the second time in two minutes he had all
too clearly displayed their weakness with his youthful boast of
freedom, and this time they had no defence but to ignore him. For a
few seconds there was a painful, uneasy silence, and then Turner
looked at Mrs Kenyon and said, in a confidential tone,—
What does Eleanor say about it all? I suppose you've asked her
advice?
She thinks he'll be against it, Mrs Kenyon said timidly. But nothing
has been said to him as yet. She—she would like Hubert to go away
—but I can't see how—even if we accepted.... She glanced at Arthur
as she concluded.
Oh, well, Turner replied, standing up, we'll have to leave it at that
presumably. No good in our interfering, obviously. And he looked at
his wife, who began to fumble her work into an untidy bundle,
preparatory to getting to her feet.
With our own trouble hanging over us, she remarked allusively, and
added, What's going to happen to poor Ken, I don't know. He's
determined that he won't come to live here.
They were all standing now, saying good-night, but Joe Kenyon
lagged behind with Arthur as they trailed across the spaces of the
drawing-room.
I'm afraid it's no good, you know, he murmured, very generous of
you to make the offer, all the same.
When he was alone in his own delightful bedroom, Arthur stood at
the open window, listening to the sound of the rain and inhaling the
welcome scents of the grateful earth. Already his mood of
resentment against these four impotent old people had passed. They
had snubbed and checked him, given him to understand that though
he might, indeed, know something of the facts of their position, he
knew nothing of the spirit. But he could not cherish anger against
them, nor even contempt. They had been in shackles too long; he
could not reasonably expect them to enter with him into any kind of
conspiracy against the old man. They were so helpless, so completely
dependent upon his goodwill. Nevertheless, although they had given
him no authority, he meant to persist in his endeavour although he
risked expulsion from this Paradise of comfort and well-being. He
was genuinely anxious to help his uncle, aunt, and cousin, and he
thrilled at the thought of crossing swords with Miss Kenyon. If he
defeated her, it would, indeed, be a glorious victory.
And, possibly, Eleanor would be on his side? He had an amazingly
clear picture of her in his mind, a forlorn, independent child, in the
midst of the splendours of the Hartling hall. He could see her
standing by the side of the colossal elephant's pad; an amazing
contrast between the slender and the gross.
What was it his uncle had called her? A lovely, solemn little chit?
Yes, she was lovely. He had hardly realised it until now. Perhaps she
would change her opinion of him after to-morrow.
VIII
VIII
Arthur's usual hour for his morning interview with old Mr Kenyon was
11 o'clock, but two or three times a week he received a message
either at breakfast or immediately after, releasing him from
attendance. He had been prepared for such a reprieve this morning,
imagining that the old man might be a trifle exhausted by his
passage of arms with Kenyon Turner the day before, but as no
message arrived he went into the library to read the morning papers
for an hour and a half before going upstairs.
All the important journals were taken at Hartling, most of them in
duplicate; and Arthur was probably the only member of the
household who had ever considered the expense involved. He had
calculated once that, including magazines and other periodicals,
more than a hundred pounds a year were spent under this head
alone. But the expenditure of the place was all on the same
magnificent scale. Arthur remembered his uncle's whimsical
comment that cigars were not provided in the workhouse, and
smiled grimly at the thought that the inmates of Hartling were the
most pampered paupers in the world.
The library was empty that morning. Arthur generally found Hubert
there at that time, but he had presumably had breakfast even earlier
than usual and gone out. Nor did Mr Turner, who came in half an
hour later, settle himself down there to his customary study of the
Times. Instead he nodded a curt good-morning to Arthur, selected
half a dozen papers, and immediately retired with them to some
other room.
After that Arthur was left severely alone. The inference was clear
enough: the Kenyons did not wish to appear in the cause he was
going to plead. They might approve his intention but they preferred
not to influence it. If he failed, they would deny any kind of
responsibility for what he had said. Their attitude had been
foreshadowed in the course of their conversation the previous night.
No good our interfering, Turner had said. They were afraid of
being dismissed from their luxurious almshouse.
Arthur put down his paper, walked across to the window, and stood
there looking out into the gardens. It had rained heavily in the night
and there was more rain coming. Low wisps of ashen gray cloud
were travelling intently across the dark purples of the heavy
background, and the horizon was hidden by the mist of an
approaching downpour. It was not a day, he reflected, remembering
many such days, to spend in going from house to house through
fountains of London mud; nor in receiving poor patients at the
surgery. How their wet clothes reeked! They brought all the worst of
the weather in with them, the mud and the wet invaded the
consulting room; one was never dry or clean on such days as these.
Instinctively he rubbed his hands together, and then looked down at
them. They were better kept than when he had first come to
Hartling; it had been impossible to keep his hands like that in
Peckham. He liked the brown of their tan, deeper on the back than
at the finger tips, and his nails were rather good. It was worth while
now to spend a little time on them.
Were the Kenyons to be pitied? They were not free, of course, but
no one was free. They were certainly more free living their life here
than he would be if he went back to Peckham. It was a dog's life
that, even Somers couldn't deny it.
The tall trees in the garden were bent by a rush of wind, and the
rain suddenly spattered furiously against the plate glass of the
window. How protected one was here! Hartling windows did not
rattle in the gale, nor let in the wet. A day such as this gave a zest
to the comfort of it all. And although one could not go out there was
plenty to do, any amount of books to read, billiards with Turner, and
probably they would play bridge in the afternoon—his uncle, Turner,
and Elizabeth all played quite a good game....
If the old man turned him out for interfering in a matter in which he
was not concerned, he would have to go back to Somers for a night
or two. If he was not very careful with the little money still left to
him, he would have to give up the idea of Canada altogether. Living
in a place like this for five weeks changed one's scale of values. He
did not look forward to roughing it so much as he had before he
came away from Peckham.
Was he pledged in any way to plead Hubert's cause with his
grandfather? Would it not be better from every point of view to leave
it alone? If Hubert's own family would not put in a word for him,
why should a comparative stranger interfere? The old man would
almost certainly be annoyed. How on earth could one open the
subject to him without impertinence? That offer last night had been
made in a moment of sentimental benevolence. He had been worked
up by that pathetic story Uncle Joe had told him, and they—he
bunched the whole Kenyon family together in this thought of them—
could not blame him if he backed out at the last minute. They could
not put on airs in that connection. His only regret would be that Miss
Kenyon would score. He would have liked to have beaten her, but
what possible chance had he of doing that? The fact was that he
was standing it all to nothing. He would be a damned fool to risk
being turned out of Hartling just now, for the sake of a romantic
notion of generosity. It was not as if his pleading were likely to help
Hubert; it would probably make things ten times worse for him by
putting the old man's back up....
He heard the mellow chime of the hall clock striking eleven, and
reluctantly turned to the door. He passed through the main drawing-
room on his way, and found all the family except Hubert and Eleanor,
sitting there engrossed in their usual occupations. None of them
spoke to him as he passed through. Miss Kenyon looked up at him
for a moment as he came in, but he could not decide whether her
expression was one of challenge or confidence in her own ability to
get what she wanted.
As he slowly mounted the wide staircase he still saw them all in
imagination, waiting with a rather pleasurable excitement for the
news of his interview. No doubt they knew well enough that he was
going to sign the order for his own exile. Had they waited in just the
same way when James Kenyon had defied his father twenty-five
years earlier?
He paused half-way up the stairs and looked down into the hall. He
could see the great elephant's pad standing there, with an effect of
gross and imperturbable solidity. Since last night, he had come in
some odd way to associate that clumping thing with Eleanor. He
could almost see her now, a slender, solemn child, dusty with recent
travel, waiting to learn her destiny....
And it was Eleanor whom he saw first when he entered Mr Kenyon's
suite of apartments. She had answered his knock—no one went into
those rooms without knocking—and he found her standing near the
door with an effect of impatience.
Are you going to say anything to him about Hubert? she asked at
once in a low voice.
Arthur hesitated before he said, I've been thinking that perhaps, on
the whole, it would be better if I didn't. It might make it worse for
him. I've no sort of influence with Mr Kenyon, I mean.
She looked at him suspiciously. He could not mistake the doubt in
her eyes. She did not believe in the excuse that he had put forward.
She had always mistrusted him for some reason or other.
Well, have I? he persisted feebly.
None whatever, I should imagine, she said; only, I thought....
She paused and looked towards the closed door of the inner room.
You're ten minutes late now, she added inconsequently.
He was irated by her attitude towards him, her dismissal of him as a
person of no importance. He longed to show her that he was not a
man to be lightly despised. But all he could find to say was a foolish,
petulant accusation of her own motives. Had she not impugned his?
No doubt you would be glad enough to see me turned out, he
said, with an almost childlike sullenness. You've always disliked
me.
She stood quite still, staring past him towards the door of her
grandfather's room. She was again wearing the dress of pale gray
linen in which he had first seen her; and she looked exquisitely
sweet, fresh, and young. But he was glad that he had been rude to
her. By that rudeness he had shown that he thought of her, and that
he resented her opinion of him. He would sooner that she hated him
than that she should be indifferent.
You think, then, she said, after what seemed to be a long pause,
that you might get—turned out, if you said anything to my
grandfather about Hubert? You know enough for that?
I suppose I know pretty nearly everything there is to know now,
he replied sulkily.
She looked at him quickly, and then turned her eyes away again.
Uncle Joe told you? she asked.
With some vague idea of loyalty in his mind, Arthur tried to
exculpate his uncle by saying, Partly, yes; but he had nothing to do
with the suggestion of my speaking to Mr Kenyon about Hubert.
No, of course not, Eleanor said; and in any case you've decided
not to.
He thought there was still a hint of question in her tone, as if she
still hoped that he might be persuaded to champion his cousin's
cause; and he grasped the opportunity to get back to the point she
had, as he believed, deliberately passed by.
You admit that I shan't do any good to Hubert, he said. Why are
you so anxious that I should get myself into trouble by interfering—
unless it is that you want to be rid of me? Because if that's all, I can
go at any time of my own free will.
I don't want you to go, she said coldly.
Then why are you so keen on—on my taking the chance of
offending Mr Kenyon? he insisted.
She faced him with a cool, steady stare. You can't seriously
believe, she said, that I should be so mean and small as to
persuade you into this for any purely selfish purpose of my own?
Why, none of them would be as paltry as that.
He blushed, but he would not drop his eyes from hers. I'm to
respect your motives, of course, he said defiantly; but you're at
liberty to impute any sort of cowardice to me?
Isn't it cowardice then? she asked, returning his stare without
flinching. Haven't you changed your mind because you're afraid of
having to leave here?
She had defeated him; and realising that he dared not answer that
question truthfully, he sought refuge in a youthful petulance. Oh!
very well, he said, turning his back on her, and crossing the room
towards the inner door. Have it your own way. You can think
anything you like about me. I don't care. He knocked and then
entered Mr Kenyon's room, without looking back to see what effect
this speech might have on her. He was persuaded that he did not
care any longer what she thought of him. She was so confoundedly
self-sufficient and superior.
Mr Kenyon was reading the Times, a thing he could do without the
aid of glasses. His sight and hearing were apparently as good as
Arthur's own. But he dropped the paper on his knees as Arthur came
in.
You've been having a talk with Eleanor? he remarked in his usual
friendly tone. What a wonderful girl she is, isn't she? I'm surprised
that you and she don't get on better together. I had hoped you
might be friends.
Arthur was slightly taken aback. It had never occurred to him that
the old man might wish him to be on more friendly terms with
Eleanor. He had never before referred to the subject in any way. Had
he, perhaps, heard or guessed at the quarrel between them in the
next room?
I'm afraid she doesn't like me, he explained.
Oh! in that case there's nothing more to be said, the old man
replied quietly. Well, you needn't stay this morning, if you've
anything else to do. I had meant to send you a message.
Arthur understood that he was dismissed, that he might now go
back and explain to the people downstairs that he had been given
no opportunity to act as the family's catspaw that morning. For
twenty-four hours at least he was relieved from any kind of
obligation, and in the meantime he could re-discuss the whole
question with Hubert and his father. There was but one objection to
this plan; he would have to tell Eleanor as he returned through the
next room.
He sighed and stood irresolute. Mr Kenyon had returned to his study
of the Times. No encouragement could be hoped from that quarter.
The old man had an amazing gift of detaching his interest from his
surroundings. He had probably forgotten that his attendant was still
in the room. Why could not Eleanor have undertaken this mission
herself? Oh! obviously because she knew that it was futile,
purposeless, utterly foolish. Nevertheless, he was not going to be
accused of cowardice, nor of trying to propitiate the old man for the
sake of being remembered in his will.
Might I speak to you a minute, sir? Arthur made his opening curtly,
almost contemptuously. By the very act of asking the question he
had regained his freedom. He saw that his fear and respect of the
old man before him were based on nothing but the longing for
comfort and luxury, for abundance and idleness. Now that he had
resolved to leave Hartling rather than endure the accusation of
cowardice, all his fears had slipped from him.
Mr Kenyon put down his paper and looked up. His pale blue eyes
were suddenly intent, the eyes of a hunting animal or a bird of prey,
in sight but not yet sure of its quarry.
Sit down, Arthur, he said quietly, pointing to a chair nearly opposite
to his own. You may speak for an hour if you wish. I have nothing
to do this morning.
It was about Hubert, Arthur said, accepting the invitation to sit
down. He did not care now, so far as he himself was concerned,
what was the upshot of this conversation, but while he was about it
he might as well do his best for poor old Hubert.
Mr Kenyon nodded, gravely attentive.
No doubt, sir, you'll wonder what concern it is of mine, Arthur
continued, but the truth is that I like Hubert, and I'm rather sorry
for him....
Sorry for him? Mr Kenyon repeated with a faint surprise.
We young men of the present generation, sir, Arthur explained,
revelling now in his sense of liberty, think a great deal of our
freedom. I don't mean to say that Hubert has any particular
ambition in that direction. He was brought up in a different
atmosphere. But from my point of view, you see, his life seems
dreadfully confined and limited, though perhaps it is a trifle
presumptuous for me to be sorry for him on that account.
And you wish ...? Mr Kenyon suggested, without the least sign of
displeasure.
Oh, well! that's another matter, Arthur said. The fact is, sir, that
Hubert has fallen in love, and for some reason that I can't pretend to
understand, neither he nor my uncle seem to care about coming to
ask your consent to his marriage. So—so I've come to plead his
cause for him.
Who is the girl he wants to marry? Mr Kenyon put in. A change
had come over him in the course of Arthur's last sentences. He sat
less stiffly in his chair; he had the air of a man re-confronted by
some familiar trouble with which he had often battled in the past.
Her name is Dorothy Martin, Arthur began. She....
Mr Kenyon interrupted him with a gesture of his hand. I know, he
said, her father is Lord Massey's agent—a homely fellow and rather
stupid. So Hubert wants to marry Miss Martin, does he? His head
drooped a little forward and he began to slide his hands slowly
backward and forward along his knees.
Arthur felt suddenly sorry for him. Neither Hubert nor his father had
told him that Miss Martin's father was, to put it bluntly, not in the
Kenyons' class. He understood better now why they had hesitated to
approach the old man. And how decently he had taken it! Without
any sign of anger, even of vexation.
I believe he's very much in love with her, Arthur murmured.
Mr Kenyon sighed and sat up. As you remarked just now, Arthur,
he said, you naturally can't be expected to understand, and I
wonder if it would be indiscreet of a very, very old man to enlighten
you?
His expression as he spoke was pathetic, wistful; he looked at Arthur
as if he besought him to approve the offered confidence.
You may be absolutely sure, sir, that I shall not repeat anything you
care to tell me, Arthur assured him.
Nor let it affect your relations with my family? the old man added,
and then while Arthur still sought a convincing reply to that rather
difficult question, went on: We are necessarily lonely in our old age,
my boy, but I sometimes wonder if my case is not in some ways
unusual. Or is it that I have suffered for overstepping the reasonable
limit of mortality? He rose from his chair as he spoke and began to
pace slowly up and down the room.
I have taken a peculiar fancy to you, Arthur, he continued after a
brief pause, and I need not be ashamed to tell you why; it is
because I admire the independence of your spirit. I liked the way
you spoke to me just now; your disregard of what might have been
against your own interests; your championship of Hubert. I could
wish—I have often wished—that there was more of the same spirit
in my own family.
Arthur flushed with pleasure. But it seemed to him that he
understood now, finally, conclusively, the secret of the Kenyons.
They were all cowards, and the old man despised them for their
cowardice; not one of them had ever had the courage to stand up to
him. If he had, in a sense, bullied them, it was because he had tried
to stimulate them into some show of active response. Nevertheless,
Arthur attempted an excuse for them.
Perhaps, sir, he said, if they had had to face the world as I
have....
Mr Kenyon had paused in his walk and now stood in front of him,
gravely attentive. But as Arthur hesitated, trying to frame a
statement that should not sound too boastful, the old man held up
his hand.
Well, well, he said, I don't wish to discuss my family with you. My
purpose is more selfish than that. I only want you not to misjudge
me, as you might very reasonably do, in the circumstances.
Downstairs, no doubt, I may sometimes appear in the light of an
autocrat. And he lifted his head with a little jerk that wonderfully
expressed his own awareness of the absurdity of that accusation.
You see, my boy, he went on, resuming his deliberate pacing of
the room, I have long been aware that none of my children, unless
it be Esther, resemble me in character. They are not, he smiled with
an air of excusing his choice of a metaphor, not fighters. There was
my poor boy James, Eleanor's father. I don't know if they have told
you anything about him?
I have heard something, Arthur admitted.
Oh, well! then you will understand what a grief his career was to
me, Mr Kenyon said with a sigh. I knew his weakness better than
he knew it himself, he continued reflectively, but he would not
listen to me. I've been forced to take care of them all, because they
are none of them able to take care of themselves. I would have
saved James, too, if he would have let me. And all I insist upon, in
return, is that they should stay here with me, where I can, in a
sense, watch over them. Perhaps I'm getting senile. The old habit of
thought is too strong in me. If I let them go out into the world, at
their age, they would surely be safe enough; but the thought of it
arouses all my old uneasiness. But in any case it can't be long now.
He had fallen into a brooding monotone, as if he spoke his thoughts
aloud; and now he raised himself with an effort and stared at Arthur
as though he had become suddenly aware of his presence in the
room.
So Hubert wants to marry Miss Martin, does he? he asked,
returning to the point at issue; and has sent you to plead for him.
No, he didn't send me, sir, Arthur explained. It was entirely my
own idea.
Mr Kenyon smiled paternally. Rash youth! rash youth! he said.
Have you no battles of your own to fight?
Well, at the moment, no sir, Arthur replied, I have been having a
very easy time here for the last five weeks.
And now you're pining to get back into the struggle again, eh? Mr
Kenyon said, with a lift of his eyebrows. Well, youth and senility are
the ages of selfishness, and when there comes a clash between
them it is senility that always must give way. And yet, Arthur, I
should be so glad if you could stay with me—till the end. I gave you
my reasons when we first talked the matter over together. I can add
still another, now; I've taken a great liking for you. Are you
absolutely determined to go?
I? No, sir. I didn't mean ... Arthur stammered.
The old man was watching him keenly. But you don't deny that you
had that in your mind, when you began to speak to me about
Hubert? he said, and then, reading confirmation of that statement
in Arthur's embarrassment, he came up to him and laid his hands on
his shoulders.
Natural enough; natural enough, my boy, he said, there's nothing
to be ashamed of. And I wouldn't ask you to make the sacrifice if I
were a younger man. But as it is what difference will a year, two
years at most, make to you at your time of life? Come, now, he
smiled with a flash of roguery, let's make a bargain! Your friend
Hubert shall have his Miss Martin, if you'll promise to stay with me
and perform those little duties I mentioned when I'm gone.
Oh, of course, sir, rather, Arthur said, blushing with pleasure and
embarrassment. I would promise that in any case. There's no need
for any—any quid pro quo, I mean.
Mr Kenyon still had his hands on the young man's shoulders, and he
gave him a gentle shake as he said, Very well, that's a bargain
then; and I may tell you that you've taken a great weight off my
mind. Now, go and tell Hubert to come up to me. I'll promise to let
him off more lightly than he deserves.
Arthur strode out of the room with the conscious pride of one who
has all life at his feet.
Eleanor rose from the desk at which she was writing as he entered.
So you did speak to him after all? she said, searching his face with
an eager, inquiring stare.
Yes, I did. It's all right, Arthur returned, disciplining his expression
of triumph to a becoming modesty. He wants to see Hubert now.
He has promised to let him off lightly, he said.
And you're staying on? Eleanor inquired.
Yes. He—he made me promise that. Arthur found himself
inexplicably dropping into apology. I couldn't possibly refuse him,
could I? You see he wants me to be here—at the end.
I understand, Eleanor said coldly, turning her back on him and
reseating herself at the desk. Will you give Hubert the message or
shall I send some one?
I'll go, Arthur replied curtly.
He was suddenly vexed and disheartened. She had dispersed all the
glamour of his achievement; had made him feel as if he had done a
mean rather than a splendid thing. There could be but one
explanation of her attitude—she suspected him of working on her
grandfather's affections. No doubt she knew that he had become a
special favourite; had known it probably before he knew it himself.
Yet even so, if there were no jealousy on her part—and Uncle Joe
had made it certain last night that her motives were above suspicion
—why should she be so annoyed? Was she afraid that he might be
designing to cut out the rest of the family?
He had reached the hall when that explanation came to him, and he
paused there, burnt with shame by the bare thought of such a
suspicion. It was degrading, infamous. He felt that he could not
endure that she should hold such an opinion of him for another
moment. He turned back towards the staircase with the intention of
instantly challenging her, and then a better means of vindication
occurred to him, and he went on into the drawing-room.
They were all there now, except Eleanor; and they made no attempt
to disguise their interest and excitement. They faced the door with
what seemed to be a concerted movement as he entered—and at
once misread the signs of his still evident emotion.
Miss Kenyon, indeed, made so sure of the correctness of her
inference that she acted upon it without further consideration.
Arthur saw her then, he believed, in her true character. She rose and
came towards him across the room with an effect of vindictive
triumph. Her pale blue eyes were bright, the pupils contracted
almost to a pin-point; they were the eyes of some fierce bird that is
at last within sight of the kill.
Well? she said in a clear, cold voice, so you've seen my father.
Arthur made no attempt to prevaricate. Yes, he wants to see
Hubert, he said, and looked across the room at his cousin as he
added, I understand that he won't raise any objection. He saw, as
he spoke, the lift of Hubert's head and the quick change of his
expression, before his attention was snatched back to Miss Kenyon.
And you? she asked sharply.
There was no need to put the question more plainly. He knew they
all knew what she meant.
Your father has asked me to stay on—indefinitely, he said quietly.
She made no reply, but she instantly veiled her eyes, lowering her
glance to the simple brooch she was wearing at her breast, at the
same time putting up a hand as if to adjust it. And when she looked
up again her expression betrayed no sign of anger or resentment.
He was disappointed. He had expected, even hoped, for some
indication of defeat from her. Vaguely he had pictured her going up
to her father to enter a violent protest. This apparently meek
submission annoyed him.
I'm sorry to disappoint you, he said provocatively.
I have forgotten the meaning of the word disappointment, she
returned gravely, looked him full in the eyes for a moment, and then
passed on towards the door.
Her self-control was superb, but the picture that remained in Arthur's
mind was of her advance towards him across the room. For one
instant he had been afraid of her.
I say! is it all right, do you think? Hubert eagerly asked, as Arthur
joined the group at the farther end of the room.
Perfectly all right, old chap—I believe, Arthur replied. Hadn't you
better toddle up and see him at once?
But what did you tell him? Hubert persisted.
Everything I knew, Arthur said. Cut along.
I suppose you're very proud of yourself? Elizabeth put in demurely
as her brother went out.
I'm very glad for Hubert's sake, was Arthur's amendment.
Only for his sake? Elizabeth commented carelessly.
Turner, with the Times on his knees, was thoughtfully twisting his
neat little moustache. So you're going to stay on indefinitely? he
remarked.
Well, yes; that's to say Mr Kenyon said he would like me to, Arthur
replied rather lamely. He was aware of a sense of antagonism
between him and the others. None of them so far had shown the
least inclination to thank him for acting as their catspaw. All they
thought about apparently was the fact that he was going to remain
permanently at Hartling. And he knew that the time had come to
vindicate his motives, to express that purpose which had come to
him in the hall when he relinquished the idea of confronting and, if
possible, confounding Eleanor.
He drew up a chair and sat down, with an air that he felt claimed his
right to be included in the family conclave.
I wonder if you'll let me say something to you all about a rather
delicate matter? he asked, looking at his uncle.
Joe Kenyon raised himself uneasily in his chair and glanced round
the faces of the little circle. They were all alert now. There could be
no question that they correctly anticipated the nature of the matter
the new-comer was going to discuss, although they were uncertain
what precisely he might have to say about it.
Yes, Arthur, yes. Say anything you like, Joe Kenyon replied rather
doubtfully. Now we know that you've come to stay for good, of
course, there's no reason why you shouldn't have—well—our
confidence.
I don't want that, Arthur said. I want to give you mine. I feel, you
know, in a confoundedly awkward position, and I'd like to clear it up
if I could. I do want you all to understand more particularly that I'm
—that I'm not 'on the make' in this business.
He paused a moment, but no one made any comment—unless
Turner's slight nod of the head could be regarded as an invitation for
him to continue.
I feel, you see, for one thing, Arthur went on, that I am in a
sense at least an outsider, not one of the family anyhow, and I do
realise too that the circumstances are pretty well unique. So what I
thought of proposing was that I should make some sort of
undertaking—I'd put it in writing—that if by any extraordinary
chance I should be—be specially favoured later, if you know what I
mean, I would hand most of anything I got, back to the family. I
should think we could get some sort of binding deed drawn up to
that effect, couldn't we?
Not until he stopped speaking did Arthur see how terribly he had
embarrassed them by thus naming the secret thing in public. Mrs
Turner was fumbling with her work; her husband leaned back in his
chair, staring up at the ceiling; and Elizabeth, flushing slightly, got up
and walked over to the window.
It was his aunt who answered him, however, indirectly. Perhaps
we'd better go into another room, Catherine, she said, addressing
her sister-in-law. I've never been able to understand legal affairs,
and this proposal of Arthur's, so far as I understand it, seems to be
something of the kind.
Mrs Turner grabbed her work and got up with a nod of agreement,
but then some purpose seemed to stiffen her. She hesitated, nearly
dropped the bead bag she was making, and said in a scarcely
audible voice, But we do appreciate the spirit of it all the same.
Oh, rather! of course, her brother echoed her.
Turner returned to that as an opening, when the three men were left
alone to discuss the proposition that had been vaguely indicated.
Very decent of you, Woodroffe, he said; and you put the thing
quite delicately too; but you understand, don't you, that it would
never do to have any kind of formal agreement?
I don't. I should prefer it to be as formal and binding as possible,
Arthur protested.
Joe Kenyon shook his head. No, no, it would never do, he said.
You see, my boy, the old man might think we'd been influencing
you.
Good Lord! I'd make that clear enough to him, Arthur exclaimed.
The two older men exchanged a smile that pitied his innocence.
You don't know him, Turner remarked caustically.
Arthur was a trifle disgusted. He was still warm with gratitude to the
old man who had treated him so delightfully that morning, and he
resented the bitter note of aspersion in Turner's voice.
He has been most frightfully decent to me, he said coldly.
Joe Kenyon began to drum on the arm of his chair. Well, no need to
go into that, eh, Charles? he asked nervously. The point is—what
we've got to make clear to Arthur comes to this, that we're quite
glad, what! to trust his word without any damned deeds and so on?
Oh, quite! quite! Turner agreed.
But you know ... Arthur began to protest.
My dear chap, Turner interrupted him, if we can trust you to do
the straight thing that's surely all that's necessary. Shake hands on
it, if you like; but no parchments, for the Lord's sake.
Very good of you, Arthur mumbled, a little overwhelmed by this
evidence of their faith in him.
If we hadn't trusted you, I couldn't have said what I did last night,
his uncle put in. And I for one am very grateful to you for
interfering in Hubert's affair. He sighed profoundly as he concluded:
It will help him in some ways, I don't doubt.
There was apparently nothing more to be said, and Arthur was on
his feet preparing to go when Turner remarked casually to his
brother-in-law, Totting 'em up pretty fast just now, isn't he? That'll
make three more of us if poor old Ken has to come in.
Joe Kenyon's only reply was to draw down the corners of his mouth
and raise his eyebrows.
Arthur did not want to hear any more. He was sorry that he had
heard so much. These petty criticisms of old Kenyon made him
despise Turner and his uncle; they represented another aspect of
their cowardice. Damn it, the old man was worth the lot of them, if
you excluded Eleanor.
He supposed that she would hear of his agreement with the family,
and wondered if she would apologise to him.
IX
IX
Arthur received a letter from Somers by the second post. It was still
raining, and he was playing billiards with Turner when the letter
arrived, so he did not open it until after tea.
Somers had written in a mood of depression. Bates, Arthur's
successor at the Peckham surgery, was not a success. The fool
means well, too well, Somers wrote; but I was wrong in
anticipating that the panel patients would like him. They don't. They
have taken his measure, and all his good intentions can't disguise
the fact that he is pudden-headed. When are you going to Canada?
If you are going? Isn't that visit of yours being amazingly protracted?
I suppose you're lapped in luxury and can't tear yourself away. Or
have you got a permanent job there as tame medico to the old man?
Or is it a girl? I wish to God you would write and tell me in any case.
I can't keep Bates (he has got on my nerves) and I should like to
know for certain if there is the least hope of your coming back. I
can't see you marrying for money, and if the hypothecated girl is the
right sort, she would face the world with you on five hundred a year.
I might make it up to that. The private practice is better than it was.
Sackville, who has been here so long, is getting too old. You and I
between us would get pretty nearly all the new people. And if my
first guess was the right one and you've got some sort of sinecure in
the Hartling household, the sooner you chuck it the better, my son.
For one thing you'll get soft, and for another you'll get no
experience. If you were doing hospital work (which you ought to
be), I should not try to tempt you away, but if you are just letting
your mind rot, I shall think it is my duty to save you at any cost.
As he read, Arthur lost the sense of his surroundings. He visualised
the narrow sitting-room of the little Peckham house, and heard
Somers's voice telling him that he ought to be doing hospital work or
getting varied experience in a general practice; that he was
becoming soft, going to pieces from a professional point of view. He
blushed like a student under the rebuke of the demonstrator.
Then he looked up and the illusion vanished. He saw that all his
circumstances were now changed. All that advice would be sound
enough if he were forced to return to such a general practice as
Peckham. But if the old man left him, say £10,000, he might have a
shot for his Fellowship; try for a registrarship at one of the bigger
hospitals; perhaps get on the staff of one and set up in Wimpole
Street. With a certain amount of capital, this would be so much
easier, and the war had given him a taste for minor surgery. Indeed,
it had always appealed to him more than medicine. Meanwhile, it
was true that he must not let himself get rusty. He ought to go on
reading, order some books from town; or at least have the Lancet
sent to him every Friday. He must keep himself up to date while he
was waiting. At the outside, he could not have to wait more than five
years. He would only be thirty-three then....
He paused doubtfully on that thought, but just then Hubert came in,
and the moment of uneasiness passed and was forgotten. It had
stopped raining and Hubert thought that they might put in nine
holes before dinner.
It was made clear on the way up to the links, however, that golf was
not Hubert's goal on this occasion. He had a wild hope that Miss
Martin might be found at the Club House. He had wanted, naturally
enough, to tell her at once that the engagement was to be
permitted, but his grandfather had sent him up to the farm on a job
that had kept him busy all the afternoon.
Probably did it just to tantalise me a bit, Hubert complained;
teach me that I couldn't have everything my own way.
Oh, surely not! Arthur protested. He was offended, again, by this
imputation of unworthy motives to old Mr Kenyon. I don't believe
any of you understand him, he continued warmly. We had quite a
long talk this morning and he rather came out of his shell. He may
seem a bit hard and inhuman at times, you know, but underneath,
I'm certain he's trying to do the best for everybody.
Hubert looked faintly surprised. Oh! that was the way he took you,
was it? he remarked.
There you go again, Arthur said. You, all of you, seem to have
made up your minds that—that—I don't know——
He could not complete his sentence. He could see that they all
feared the old man, but they never brought any explicit charge
against him unless it were that he bullied them into staying on at
Hartling. And all that had been explained. Arthur, remembering his
conversation of the morning, was strongly inclined now to take the
old man's side. He knew their weaknesses. They were a poor lot
obviously. They lacked independence of spirit; if they were allowed
to go out into the world they would come awful croppers like the
unfortunate, hot-headed James, Eleanor's father. The old man had
learnt a lesson in the course of that affair. He was a bit of an
autocrat, no doubt; but he had good reason to be, with a family that
could not be trusted.
Hubert appeared either unwilling or unable to provide a definition of
the family's attitude. Oh, well, he said, no good discussing that, is
it? Here we are and we've got to put up with it. And, personally, you
know, I don't care much now—partly thanks to you, old man.
Only partly, Arthur reflected, but he made no comment on that.
That's all right, then, was all he said.
Hubert was in luck, for Miss Martin was at the Club House, drawn
thither, no doubt, by the same hope that had stimulated her lover,
and although they cheerfully proposed a foursome, Arthur knew that
they would sooner be alone, and declined. The proposed fourth
player in the case was Fergusson, the general practitioner from the
village, to whom reference had been made when the post of medical
attendant had been first offered to Arthur. He and Fergusson had
met once or twice on the links, but their brief conversations had so
far been limited to golf. The doctor was a man of sixty or so, with
thick gray hair and moustache and a strong, clumsy figure. Arthur
had formed the opinion that he was rather a surly fellow.
Care to take me on for nine holes—haven't time for more? Arthur
asked him.
Fergusson nodded. Not that I'm particularly anxious to play, he
said. The ground will be very wet, I'm thinking, after all the rain
we've had to-day. I just looked in on my way home, without much
idea of getting a game. Indeed, to be honest, I've had a very long
day and am not so anxious to exert myself.
Scattered sort of practice, I expect, Arthur commented. Have a
cigar.
Fergusson accepted the cigar with a nod of thanks. One of your
perquisites? he asked, smiling rather grimly.
Arthur stiffened. Never thought of it like that, he said. They're all
over the shop up there. You just take 'em as you want 'em.
No need to get ruffled, Fergusson replied quietly. I know. I used
to be up there once a week or so before you came. Nice little
sinecure.
But I say, look here, Arthur said, suddenly conscious for the first
time that he might have been guilty of a breach of medical etiquette,
you don't mean to tell me that I've taken away one of your cases?
Fergusson laughed dryly. Well, you have and you haven't, he said.
But your conscience is no doubt clear enough and everything was
done in proper form. The old man wrote to me and explained, and I
went up and talked it all over with him. You were playing golf on
that occasion, I'm thinking. However, it'll be a soft job for you.
Arthur still looked uneasy. I never once thought about you in that
connection, you know, he said. I ought, anyhow, to have come and
Welcome to Our Bookstore - The Ultimate Destination for Book Lovers
Are you passionate about books and eager to explore new worlds of
knowledge? At our website, we offer a vast collection of books that
cater to every interest and age group. From classic literature to
specialized publications, self-help books, and children’s stories, we
have it all! Each book is a gateway to new adventures, helping you
expand your knowledge and nourish your soul
Experience Convenient and Enjoyable Book Shopping Our website is more
than just an online bookstore—it’s a bridge connecting readers to the
timeless values of culture and wisdom. With a sleek and user-friendly
interface and a smart search system, you can find your favorite books
quickly and easily. Enjoy special promotions, fast home delivery, and
a seamless shopping experience that saves you time and enhances your
love for reading.
Let us accompany you on the journey of exploring knowledge and
personal growth!
ebookgate.com

More Related Content

PDF
Entity Framework 6 Recipes 2nd Edition Brian Driscoll
PDF
Entity Framework 6 Recipes 2nd Edition Brian Driscoll
PDF
Lotusphere 2007 AD507 Leveraging the Power of Object Oriented Programming in ...
PDF
DOCX
Entity Framework
PPTX
Entity framework
PDF
Patterns And Practices For Infrastructure As Code With Examples In Python And...
PDF
Broadcom CA SDM overview - Introduction to BOP
Entity Framework 6 Recipes 2nd Edition Brian Driscoll
Entity Framework 6 Recipes 2nd Edition Brian Driscoll
Lotusphere 2007 AD507 Leveraging the Power of Object Oriented Programming in ...
Entity Framework
Entity framework
Patterns And Practices For Infrastructure As Code With Examples In Python And...
Broadcom CA SDM overview - Introduction to BOP

Similar to Entity Framework 6 Recipes 2nd Edition Brian Driscoll (20)

PDF
Programming Microsoft Sql Server 2000 With Microsoft Visual Basic Net 1st Edi...
PPT
Daniel Egan Msdn Tech Days Oc Day2
PDF
Entity Framework Core Cookbook 2nd Edition Ricardo Peres
PDF
Entity Framework Interview Questions PDF By ScholarHat
PPTX
Entity Framework 4
PDF
Elements of DDD with ASP.NET MVC & Entity Framework Code First
PDF
.NET Vs ASP.NET – Make the Right Choice for Your Next Project.pdf
PDF
Designing Data Intensive Web Applications 1st Edition Stefano Ceri
PDF
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
PDF
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
DOCX
Automatic answer checker
PDF
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
PPTX
Building nTier Applications with Entity Framework Services (Part 1)
PDF
Cis 555 Week 4 Assignment 2 Automated Teller Machine (Atm)...
PPT
Entity Framework Overview
PDF
Improved Presentation and Facade Layer Operations for Software Engineering Pr...
DOCX
PDF
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
PDF
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
PDF
Learning Airtable (First Early Release) Elliott Adams
Programming Microsoft Sql Server 2000 With Microsoft Visual Basic Net 1st Edi...
Daniel Egan Msdn Tech Days Oc Day2
Entity Framework Core Cookbook 2nd Edition Ricardo Peres
Entity Framework Interview Questions PDF By ScholarHat
Entity Framework 4
Elements of DDD with ASP.NET MVC & Entity Framework Code First
.NET Vs ASP.NET – Make the Right Choice for Your Next Project.pdf
Designing Data Intensive Web Applications 1st Edition Stefano Ceri
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
Automatic answer checker
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
Building nTier Applications with Entity Framework Services (Part 1)
Cis 555 Week 4 Assignment 2 Automated Teller Machine (Atm)...
Entity Framework Overview
Improved Presentation and Facade Layer Operations for Software Engineering Pr...
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
Programming Microsoft SQL Server 2000 with Microsoft Visual Basic NET 1st edi...
Learning Airtable (First Early Release) Elliott Adams
Ad

Recently uploaded (20)

PDF
Complications of Minimal Access Surgery at WLH
PDF
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
PDF
Chapter 2 Heredity, Prenatal Development, and Birth.pdf
PDF
Basic Mud Logging Guide for educational purpose
PPTX
Renaissance Architecture: A Journey from Faith to Humanism
PDF
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
PDF
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
PDF
TR - Agricultural Crops Production NC III.pdf
PPTX
Microbial diseases, their pathogenesis and prophylaxis
PPTX
Final Presentation General Medicine 03-08-2024.pptx
PPTX
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
PPTX
master seminar digital applications in india
PPTX
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
PDF
RMMM.pdf make it easy to upload and study
PDF
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
PDF
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
PDF
VCE English Exam - Section C Student Revision Booklet
PPTX
Cell Structure & Organelles in detailed.
PDF
STATICS OF THE RIGID BODIES Hibbelers.pdf
PDF
O7-L3 Supply Chain Operations - ICLT Program
Complications of Minimal Access Surgery at WLH
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
Chapter 2 Heredity, Prenatal Development, and Birth.pdf
Basic Mud Logging Guide for educational purpose
Renaissance Architecture: A Journey from Faith to Humanism
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
TR - Agricultural Crops Production NC III.pdf
Microbial diseases, their pathogenesis and prophylaxis
Final Presentation General Medicine 03-08-2024.pptx
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
master seminar digital applications in india
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
RMMM.pdf make it easy to upload and study
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
VCE English Exam - Section C Student Revision Booklet
Cell Structure & Organelles in detailed.
STATICS OF THE RIGID BODIES Hibbelers.pdf
O7-L3 Supply Chain Operations - ICLT Program
Ad

Entity Framework 6 Recipes 2nd Edition Brian Driscoll

  • 1. Entity Framework 6 Recipes 2nd Edition Brian Driscoll download https://ebookgate.com/product/entity-framework-6-recipes-2nd- edition-brian-driscoll/ Get Instant Ebook Downloads – Browse at https://ebookgate.com
  • 2. Get Your Digital Files Instantly: PDF, ePub, MOBI and More Quick Digital Downloads: PDF, ePub, MOBI and Other Formats Entity Framework 4 in Action Stefano Mostarda https://ebookgate.com/product/entity-framework-4-in-action- stefano-mostarda/ Entity Framework Core in Action Second Edition Jon P Smith https://ebookgate.com/product/entity-framework-core-in-action- second-edition-jon-p-smith/ Web Development Recipes 2nd Edition Edition Brian P. Hogan https://ebookgate.com/product/web-development-recipes-2nd- edition-edition-brian-p-hogan/ WCF 4 5 Multi Layer Services Development with Entity Framework Third Edition Mike Liu https://ebookgate.com/product/wcf-4-5-multi-layer-services- development-with-entity-framework-third-edition-mike-liu/
  • 3. WCF 4 5 Multi Layer Services Development with Entity Framework 3rd New edition Edition Liu Mike https://ebookgate.com/product/wcf-4-5-multi-layer-services- development-with-entity-framework-3rd-new-edition-edition-liu- mike/ Web Development Recipes Second Edition Brian P. Hogan Et Al. https://ebookgate.com/product/web-development-recipes-second- edition-brian-p-hogan-et-al/ Web Based Training Creating e Learning Experiences 2nd Edition Margaret Driscoll https://ebookgate.com/product/web-based-training-creating-e- learning-experiences-2nd-edition-margaret-driscoll/ Schwarz Christoffel Mapping 1st Edition Tobin A. Driscoll https://ebookgate.com/product/schwarz-christoffel-mapping-1st- edition-tobin-a-driscoll/ Database Design Using Entity Relationship Diagrams Second Edition Bagui https://ebookgate.com/product/database-design-using-entity- relationship-diagrams-second-edition-bagui/
  • 5. For your convenience Apress has placed some of the front matter material after the index. Please use the Bookmarks and Contents at a Glance links to access them.
  • 6. v Contents at a Glance About the Authors ����������������������������������������������������������������������������������������������������������� xxvii About the Technical Reviewer����������������������������������������������������������������������������������������� xxix Preface���������������������������������������������������������������������������������������������������������������������������� xxxi Chapter 1: Getting Started with Entity Framework ■ ■ �����������������������������������������������������������1 Chapter 2: Entity Data Modeling Fundamentals ■ ■ ��������������������������������������������������������������11 Chapter 3: Querying an Entity Data Model ■ ■ ����������������������������������������������������������������������55 Chapter 4: Using Entity Framework in ASP.NET MVC ■ ■ �����������������������������������������������������107 Chapter 5: Loading Entities and Navigation Properties ■ ■ �������������������������������������������������129 Chapter 6: Beyond the Basics with Modeling and Inheritance ■ ■ ��������������������������������������183 Chapter 7: Working with Object Services ■ ■ ����������������������������������������������������������������������235 Chapter 8: Plain Old CLR Objects ■ ■ �����������������������������������������������������������������������������������257 Chapter 9: Using the Entity Framework in N-Tier Applications ■ ■ �������������������������������������295 Chapter 10: Stored Procedures ■ ■ �������������������������������������������������������������������������������������341 Chapter 11: Functions ■ ■ ���������������������������������������������������������������������������������������������������375 Chapter 12: Customizing Entity Framework Objects ■ ■ �����������������������������������������������������413 Chapter 13: Improving Performance ■ ■ �����������������������������������������������������������������������������451 Chapter 14: Concurrency ■ ■ �����������������������������������������������������������������������������������������������483 Index���������������������������������������������������������������������������������������������������������������������������������503
  • 7. 1 Chapter 1 Getting Started with Entity Framework When working with relational databases, we think in terms of tables with rows and columns. Tables are highly structured and excel at set-based processing. Before the wide adoption of object-oriented programming, we thought about problems “procedurally” and solved them by writing code in a structured, top-down manner, function after function. Both worlds lined up well: Tables, rows, and columns closely matched the structured and procedural patterns in our code. Life was good - for a time... Much has evolved on the code side. We now think in terms of objects and domain models. We architect, design, and program against real-world things, like customers and orders. We draw nouns in our problem space on whiteboards. We draw lines between them, denoting relationships and interactions. We build specifications and assign work to development teams in terms of these drawings. In short, we architect, design, and program at a conceptual level that is very distant from the logical and physical organization of the database. While the software development process has dramatically matured and the way in which we reason and solve problems has evolved, the database has not. The data remains locked in the same tables, rows, and columns paradigm, where it has been for many years. Unfortunately, this creates a mismatch (an impedance mismatch, as Microsoft fellow Anders Hejlsberg might call it): Object-oriented class hierarchies vs. a highly normalized database structure. To cope with this gap, software projects often introduce a “database layer” that translates application domain classes into the rows and columns saved in tables. This approach has spawned many commercial and open-source data access frameworks; all attempting to bridge the ever widening gap between evolving development processes and structured data. Interestingly, an entire new field of Object Relational Mapping (ORM) has come out it. The Entity Framework, coupled with the Language-Integrated Query (LINQ) framework, both from Microsoft, enables us to address the mismatch problem head-on. Using Entity Framework, we model entity classes for our application on a design surface or directly in code. Then we model relationships (associations) between these entities. In our code, we construct LINQ queries to program against these entities and associations. LINQ allows us to express relational database set concepts directly into our code while working in terms of entity types and associations. All of this helps to streamline our development experience while reducing the overall effort. Instead of coding large numbers of highly redundant ADO.NET data access constructs, we express our data needs in simple LINQ queries. Instead of programming against the schema of a highly normalized database, we code against entity classes. Entity Framework maps entity classes to the underlying database for you. Note ■ ■ We use the term entity class or entity object to refer to a class that typically represents a domain item in an application. Domain classes represent real-world objects, such as an Employee, Department, or Manager, which your application will represent and track. The end users and stakeholders of your application should be able to look at the domain classes in your application and say, “Yes, that’s what our business does.” Entity classes define the schema, or properties, but not the behavior, of a domain class. In essence, entity classes expose the state of an object.
  • 8. Chapter 1 ■ Getting Started with Entity Framework 2 1-1. A Brief Tour of the Entity Framework World Entity Framework is Microsoft’s strategic approach to data access technology for building software applications. Unlike earlier data access technologies, Entity Framework, coupled with Visual Studio, delivers a comprehensive, model-based ecosystem that enables you to develop a wide range of data-oriented applications, including desktop, Internet, cloud, and service-based applications, many of which will be covered in this book. The History Entity Framework is not new. The product dates back to Visual Studio 2008 and has come a long way in features and functionality. Figure 1-1 gives the pictorial history. The first version of Entity Framework was limited, featuring basic ORM support and the ability to implement a single approach known as Database First, which we thoroughly demonstrate in this book. Version 4 brought us another approach to using Entity Framework: Model First, along with full Plain Old CLR Object (POCO) support and default lazy loading behavior. Soon after, the Entity Framework team released three smaller, or point releases, 4.1 through 4.3, which represented yet another approach to using Entity Framework: Code First. As shown above, Version 5 of Entity Framework coordinated with the release of the .NET 4.5 framework and Visual Studio 2012, delivering significant performance improvements along with support for enums, table value functions, spatial types, the batch import of stored procedures, and deep support with the ASP.NET MVC framework. Now we are at Version 6 of the Entity Framework. Version 6 delivers asynchronous support for querying and updates, stored procedure support for updates in Code First, improved performance, and a long list of new features, which we will focus on in this book. Figure 1-1. A short history of the Entity Framework
  • 9. Chapter 1 ■ Getting Started with Entity Framework 3 Note ■ ■ Version 5 of Entity Framework can also be used with Visual Studio 2010. Version 6 of Entity Framework, released with Visual Studio 2013, has tooling/runtime support for Visual Studio 2012 and runtime support for Visual Studio 2010. To level set, let’s take a brief look at some of the key components of the Entity Framework ecosystem. What follows is not by any means a comprehensive description of Entity Framework; that would take hundreds of pages. We’ll look at just a few key areas to help get you oriented for the recipes that are at the heart of this book. The Model Entity Framework is a technology with a strong focus on modeling. As you model with Entity Framework, you will see many familiar genetic markers from previous technologies and patterns. For example, you will, no doubt, see a resemblance to entity-relationship diagrams and the widely adopted conceptual, logical, and physical design layering approach. The model that you create in Entity Framework is characterized by a construct called an Entity Data Model (EDM), which enables you to code against strongly typed entity classes, not database schema and objects. (Figure 1-2 shows this model in conceptual form.) The Entity Data Model enables you to customize the mappings between entity classes and database tables to move beyond the classic, one-to-one mapping, or class-to-table mapping. In Figure 1-2, note how the database tables (on the left) do not directly map to the entity classes, which we code against (on the right). Instead, the mapping capabilities built into the Entity Data Model enable the developer to code against a set of entity classes that more closely resemble the problem domain, as opposed to a highly normalized database, designed for performance, scalability, and maintainability. For example, note above how the Employees, Devices, and Phone numbers) are physically stored in three different tables, which from a DBA perspective makes perfect sense. But the developer codes against a single Employee entity class that contains a collection of Devices and Phone Numbers. From a developer and project Figure 1-2. The Entity Data Model
  • 10. Chapter 1 ■ Getting Started with Entity Framework 4 stakeholder perspective, an employee is a single object, which happens to contain phone numbers and devices. The developer is unaware, and does not care, that the DBA has normalized this employee object into three separate database tables. Once configured, the mapping between the single class and three database tables is abstracted away and handled by the Entity Framework. A reverse situation can be seen for the single Department table, which programmatically maps to three entity classes that represent individual departments. Again, to the developer and project stakeholders, a separate entity object represents each department (Accounting, Marketing, Finance, and so on), but DBA optimizes and collapses these objects into a single database table for data storage purposes. Of course, as can be seen in the Location table, you can easily map a single entity class to a single database table, which is the default behavior for Entity Framework. The key takeaway here is that developer and project stakeholders work with a representation of domain classes that make sense in the context of the application. The DBA can structure the underlying database tables in order to efficiently tune the database. And you can easily bridge these two worlds with the Entity Framework. The Layers The Entity Data Model consists of three separate layers: the conceptual, store, and mapping layers. Each layer is decoupled from the others. The entity classes are contained in the conceptual layer of the Entity Data Model. This is layer in which developers and project stakeholders work. Depending upon how you implement the Entity Framework, the conceptual layer can be modeled with a designer or from code. Once you make that decision, you can reverse- engineer your model from an existing database, leveraging the designer and extensive tooling that ships with Entity Framework or create your model with code and have Entity Framework generate the database for you. The syntax for the conceptual layer is defined in the Conceptual Schema Definition Language (CSDL). Every useful application needs to persist objects to some data store. The store layer of the Entity Data Model defines the tables, columns, relationships, and data types that map to the underlying database. The Store Schema Definition Language (SSDL) defines the syntax for the store model. Finally, the mapping layer defines the mapping between the conceptual and store layer. Among other things, this layer defines how properties from entity classes map to columns in database tables. This layer is exposed to the developer from the Mapping Details window contained in the Entity Framework designer or data annotations and fluent API if choosing a code-based approach. The Mapping Specification Language (MSL) defines the syntax for the mapping layer. The Terminology As expected, the Entity Framework comes with its own vocabulary. If you have used any of the popular ORM tools or are familiar with database modeling, you’ve probably encountered some of the terminology before. Although the entire vocabulary is extensive, we’ll provide just a few of the basic terms to get us started. As discussed earlier, an EntityType represents a class in your domain model. An instance of an EntityType is often referred to as an entity. If you are using the Entity Framework designer, an EntityType is represented on the design surface as a box with various properties. Figure 1-3 shows two EntityTypes: Employee and Task.
  • 11. Chapter 1 ■ Getting Started with Entity Framework 5 An EntityType usually has one or more properties. Just like with a class, a property is a named value with a specific data type. Properties can have simple types like integer, string, and so on; or have ComplexTypes; or be collections. Navigation properties refer to other related entities (typically represented by foreign key relationships in a database). The non-navigation properties on an EntityType are usually just called scalar properties. A relationship between two entities is called an association. Associations between EntityTypes are shown on the design surface as a line connecting the EntityTypes. The line is annotated to show the multiplicity on each end of the association. The association in Figure 1-3 is a one-to-many association between Employee and Task. An Employee can have zero or more tasks. Each Task is associated to exactly one Employee. Every EntityType has a property or set of properties that denote its EntityKey. An EntityKey uniquely identifies the entity to Entity Framework and is most often mapped to a primary key from the entity’s representation in the underlying database. Finally, no discussion on Entity Framework would be complete without mentioning the context object. The context object for Entity Framework is your gateway into the Entity Framework services. The context object exposes entity objects, manages the database connection, generates parameterized SQL, marshals data to and from the database, caches objects, helps maintain change tracking and materializes, or transforms, an untyped result set into a collection of strongly typed objects. In the beginning, there was the ObjectContext object. Now, Entity Framework supports an alternate, more streamlined context object called the DbContext. The DbContext greatly simplifies the developer experience when working with Entity Framework. Interestingly, the DbContext is a wrapper, or facade, around the ObjectContext, exposing the underlying ObjectContext functionality in an intuitive, friendly and productive way. Clearly, the DbContext is the preferred approach for working with Entity Framework as we will demonstrate in great detail in this book. The Code Despite a tremendous emphasis on visual design support, the Entity Framework is all about code. The models, EntityTypes, associations, mappings, and so on are ultimately expressed in concrete code that becomes part of your application. This code is either generated by Visual Studio and Entity Framework or created manually by the development team. You can choose quite a bit about the code-generation process or the lack of it by changing various properties on your project or modifying the underlying code-generation templates. Visual Studio uses a code-generation technology called Text Template Transformation Toolkit, simply referred to as T4 templates. The Visual Studio tooling uses T4 templates to generate, or scaffold, code automatically. The great thing about T4 template support in Visual Studio is that you can edit the templates to tailor the code-generation process to match your exact needs. This is an advanced technique, but it is necessary in some cases. We’ll show you how to do this in a few recipes. Figure 1-3. A model with Employee and Task with a one-to-many association between them
  • 12. Chapter 1 ■ Getting Started with Entity Framework 6 Alternatively, you can leverage the more recent Code-First approach to manually create the concrete code yourself, gaining direct control over the entire process. With Code First, the developer can create entity classes, mappings and context object, all without the help of a designer. These manually created entity classes, commonly referred to as POCO, or Plain Old CLR Objects, have no dependence on Entity Framework plumbing. Even more interesting, the development team can leverage the Entity Framework Power Tool utilities (free download from Microsoft) to reverse-engineer a Code First model from an existing database, foregoing the effort to have manually create the entity classes, mappings and context object. The recipes in Chapter 8 show you the basics of creating and using POCO. Many of the recipes throughout the book will show you how to use Code First across specific contexts such as in n-Tier applications. Visual Studio Of course, the main tool we use when developing applications for the Windows environment is Visual Studio. This Integrated Development Environment has evolved over many years from a simple C++ compiler and editor to a highly integrated, multi-language environment that supports the entire software development lifecycle. Visual Studio and its related tools and services provide for design, development, unit testing, debugging, software configuration management, build management and continuous integration, and much more. Don’t be worried if you haven’t used all these in your work; few developers have. The point is that Visual Studio is a full-featured toolset. Visual Studio plays a vital role in the development of Entity Framework applications. Visual Studio provides an integrated design surface for Entity Framework models. Using this design surface and other tools in Visual Studio, you can create models from scratch or create them from an existing database. You also have the option to completely eliminate the designer and manually craft your Entity Types and configuration. If you have an existing database, which is the case for many of us with existing applications, Visual Studio provides tools for importing your tables and relationships into a model. This fits nicely with the real world because few of us have the luxury of developing brand-new applications. Most of us have to extend, maintain, and evolve our existing code and databases. Alternately, you can create a model from scratch by starting with an empty design surface and adding new EntityTypes to the surface, creating both associations and inheritance hierarchies for your model. When you are done creating the model, right-click the design surface and select Generate Database from Model. If your project team is code-centric, you can instead create a set of domain classes, including relationships and a context class and then wire up these classes to hook into the Entity Framework engine and features without having to use a designer. Once you have created your model, changes often happen. That’s the nature of software development. Visual Studio provides tools for updating the model from the database. This will keep the model synchronized with changes in the database. Additionally, the Entity Framework Team also supports a tool called Code First Migrations, which can be used to keep your database up-to-date with changes in your model. 1-2. Using Entity Framework Entity Framework is tightly integrated with Visual Studio. To implement Entity Framework in your application, add a new ADO.NET Entity Data Model in your project. Right-click your project and select Add ➤ New Item. In the dialog box (see Figure 1-4), choose the ADO.NET Entity Data Model template. This template is located under the Data templates. Click Add to launch the Entity Data Model Wizard.
  • 13. Chapter 1 ■ Getting Started with Entity Framework 7 There are two options on the first page of the Entity Data Model Wizard: start with an existing database or start with an empty model. (The former option is actually labeled “Generate from database.”) This first page is shown in Figure 1-5. Figure 1-4. Adding a new model to your project
  • 14. Chapter 1 ■ Getting Started with Entity Framework 8 Generating a model from an existing database is the Database-First approach. From the tables, views, and stored procedures that you select from the underlying database, the wizard will create a model and entity classes, against which you can write code. The immediate benefit here is that you write code against strongly typed entity classes, which Entity Framework maps to the underlying database tables and columns. If the tables you include are related in the database, these relationships will be modeled as associations. This is one way to create your model if you already have a database for your application. However, if you prefer to use the Code-First approach with an existing database, worry not. The Entity Framework team has created tooling (The Entity Framework Power Tools) that reverse-engineers an existing database into domain entity classes, just as if you coded them by hand. If you’re working on a brand-new application, without an existing database, you have options as well. In the Entity Framework designer, you can start with an empty design surface. Right-click the design surface to create new EntityTypes, associations, or inheritances. You can also drag them from the Toolbox onto the design surface. Once your model is complete, just right-click the design surface and select Generate Database from Model. This will generate a script you can use to create the database tables and relationships for the model. Figure 1-5. The Entity Data Model Wizard gives you a choice between creating a model from an existing database or starting with an empty model
  • 15. Chapter 1 ■ Getting Started with Entity Framework 9 Alternately, you can manually create each of your entity classes in Visual Studio and simply register them in the DbContext object, then hook into the Entity Framework services. Entity Framework will map the classes to the underlying databases and automatically create a model in memory at runtime. With the Model-First or Database-First approaches, you use the Entity Framework designer to develop your model. The key parts of a model in the designer are shown in Figure 1-6. In this model, a Customer has a one-to-many association with an Order. Each customer may have many orders, but each order is associated with just one customer. The Mapping Details window shows that the Customer EntityType maps to the Customer table in the database. The Mapping Detail window also shows the mapping between the columns in the Customer table and the scalar properties in the Customer EntityType. Keep in mind that you can make the same kind of mapping configurations using the data annotations or fluent API features found in the Code First approach to using Entity Framework. Figure 1-6. Key parts of a model in the designer
  • 16. Chapter 1 ■ Getting Started with Entity Framework 10 Of course, there’s more to the designer and model than just the few key parts illustrated in Figure 1-6. In the recipes in this book, we’ll cover just about every aspect of using the designer to create models. In some cases, we go beyond what can be done with the designer and show you how to create models that require direct editing of the underlying .edmx file. The .edmx file contains the complete model definition, including the conceptual layer, store layer, and mapping layer. So, whether we implement Entity Framework with the Database-First, Model-First or Code-First approach, we always end up with a model. We gain significant productivity, as we can program against objects in the model (EntityTypes) as you do with other objects in your application. For the model in Figure 1-6, your code uses Customer and Order in much the same way as you use other objects. If you want to insert a new customer and order into the database, you can create instances of the Customer and Order types, set the properties, add them to the in-memory context that represents the model, and call SaveChanges(). All the necessary SQL code is generated and sent to the database to insert the rows. To retrieve customers and orders from the database, you use either LINQ or Entity SQL to create a query in terms of the EntityTypes and associations in the model. The recipes throughout this book will show you step by step how to model just about every conceivable database scenario; how to query, insert, update, and delete using these models; and how to use Entity Framework in many kinds of applications.
  • 17. 11 Chapter 2 Entity Data Modeling Fundamentals More likely than not, you are just beginning to explore Entity Framework, and you are probably asking the question, “Okay, how do I get started?” If this describes you, this chapter is a great place to start. If, on the other hand, you have built some working models and feel comfortable with a few key modeling concepts, such as entity splitting and inheritance, you can skip this chapter. In this chapter, we will walk you through the basic examples of modeling with Entity Framework. Modeling is the core feature of Entity Framework and what distinguishes Entity Framework from previous Microsoft data access platforms. Once you have built your model, you can write code against the model rather than against the rows and columns in the relational database. We start off this chapter with an example of how to create a simple conceptual model, and then let Entity Framework create the underlying database. In the remaining examples, we will show you how to create models from existing tables and relationships in your databases. 2-1. Creating a Simple Model Problem You have a brand new project, and you want to create a model. Solution Let’s imagine that you want to create an application to hold the names and phone numbers of people that you know. To keep things simple, let’s assume that you need just one entity type: Person. To create the new model, do the following: 1. Right-click your project, and select Add ➤ New Item. 2. From the templates, select ADO.NET Entity Data Model and click Add. This template is located in Data under Visual C# Items (see Figure 2-1).
  • 18. Chapter 2 ■ Entity Data Modeling Fundamentals 12 3. In the first step of the wizard, choose Empty Model and click Finish. The wizard will create a new conceptual model with an empty design surface. 4. Right-click the design surface, and select Add ➤ Entity. 5. Type Person in the Entity name field, and select the box to Create a key property. Use Id as the Key Property. Make sure that its Property Type is Int32. Click OK, and a new Person entity will appear on the design surface (see Figure 2-2). Figure 2-1. Adding a new .emdx file that contains XML describing the conceptual model, storage model, and mapping layer
  • 19. Chapter 2 ■ Entity Data Modeling Fundamentals 13 6. Right-click near the top of the Person entity, and select Add ➤ Scalar Property. A new scalar property will be added to the Person entity. 7. Rename the scalar property FirstName. Add scalar properties for LastName, MiddleName, and PhoneNumber. 8. Right-click the Id property, and select Properties. In the properties view, change the StoreGeneratedPattern property to Identity if it is not already set to Identity. This flags the Id property as a value that will be computed by the store layer (database). The database script we get at the end will flag the Id column as an identity column, and the storage model will know that the database will automatically manage the values in this column. The completed conceptual model should look like the model in Figure 2-3. Figure 2-2. Adding a new entity type representing a Person in our conceptual model
  • 20. Chapter 2 ■ Entity Data Modeling Fundamentals 14 You now have a simple conceptual model. To generate a database for our model, there are a few things we still have to do: 9. We need to change a couple of properties of our model to help with housekeeping. Right-click the design surface, and select properties. Change the Database Schema Name to Chapter2, and change the Entity Container Name to EF6RecipesContext. Figure 2-4 illustrates these changes. Figure 2-4. Changing the properties of our model Figure 2-3. Our completed model with an entity type representing a Person
  • 21. Chapter 2 ■ Entity Data Modeling Fundamentals 15 10. Right-click the design surface, and select Generate Database Script from Model. Select an existing database connection or create a new one. In Figure 2-5, we’ve opted to create a new connection to our local machine and to the database EF6Recipes. 11. Click OK to complete the connection properties, and click Next to preview the database script (see Figure 2-6). Once you click Finish, the generated script is added to your project. Figure 2-5. Creating a new database connection that will be used by Entity Framework to create a database script that we can use to create a database from our conceptual model
  • 22. Chapter 2 ■ Entity Data Modeling Fundamentals 16 12. Run the database script in an SSMS query window to create the database and the People table. How It Works The Entity Framework Designer is a powerful tool for creating and updating a conceptual model, storage model, and mapping layer. This tool provides support for bidirectional model development. You can either start with a clean design surface and create a model; or start with a database that you already have and import it to create a conceptual model, storage model, and mapping layer. The current version of the Designer supports somewhat limited roundtrip modeling, allowing you to re-create your database from a model and update the model from changes in your database. The model has a number of properties that affect what goes in the generated storage model and database script. We changed two of these properties. The first was the name of the container. This is the class derived from DbContext. We called this EF6RecipesContext to be consistent with the contexts we use throughout this book. Additionally, we changed the schema to “Chapter 2.” This represents the schema used to generate the storage model as well as the database script. Figure 2-6. Generating the storage model in the .edmx file and creating the database script
  • 23. Chapter 2 ■ Entity Data Modeling Fundamentals 17 The code in Listing 2-1 demonstrates one simple way to create and insert instances of our Person entity type. The code also demonstrates iterating through all the Person entities in our database. Listing 2-1. Inserting into and Retrieving from Our Model using (var context = new EF6RecipesContext()) { var person = new Person { FirstName = Robert, MiddleName=Allen, LastName = Doe, PhoneNumber = 867-5309 }; context.People.Add(person); person = new Person { FirstName = John, MiddleName=K., LastName = Smith, PhoneNumber = 824-3031 }; context.People.Add(person); person = new Person { FirstName = Billy, MiddleName=Albert, LastName = Minor, PhoneNumber = 907-2212 }; context.People.Add(person); person = new Person { FirstName = Kathy, MiddleName=Anne, LastName = Ryan, PhoneNumber = 722-0038 }; context.People.Add(person); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { foreach (var person in context.People) { System.Console.WriteLine({0} {1} {2}, Phone: {3}, person.FirstName, person.MiddleName, person.LastName, person.PhoneNumber); } } The output of the code in Listing 2-1 should look something like the following: John K. Smith, Phone: 824-3031 Robert Allen Doe, Phone: 867-5309 Kathy Anne Ryan, Phone: 722-0038 Billy Albert Minor, Phone: 907-2212 Best Practice When we created a new instance of the database context, we did it within a using() statement: using (var context = new EF6RecipesContext()) { ... }
  • 24. Chapter 2 ■ Entity Data Modeling Fundamentals 18 If you are not familiar with this pattern, it’s really pretty simple. Normally, when we get a new instance of an object, we use the new operator and assign the result to some variable. When the variable goes out of scope and the object is no longer referenced by anything else, the garbage collector will do its job at some point and reclaim the memory for the object. That works great for most of the objects that we create in our .NET applications because most objects hold on to resources that can wait around for whenever the garbage collector has a chance to reclaim them. The garbage collector is rather nondeterministic. It reclaims resources pretty much on its own schedule, which we can only partially influence. Instances of DbContext hold on to system resources such as database connections that we want to release as soon as we’re done with them. We don’t really want these database connections to stay open waiting for the garbage collector eventually to reclaim them. There are a few nice features of using() statements. First, when the code execution leaves the using() {} block, the Dispose() method on the context will be called because DbContext implements the IDisposable interface. For DbContext, the Dispose() method closes any active database connections and properly cleans up any other resources that need to be released. Second, no matter how the code leaves the using(){} block, the Dispose() method is called. Most importantly, this includes return statements and exceptions that may be thrown within the code block. The using(){} block is kind of a guarantee that critical resources will be reclaimed properly. The best practice here is always to wrap your code in the using(){} block when creating new instances of DbContext. It’s one more step to help bulletproof your code. 2-2. Creating a Model from an Existing Database Problem You have an existing database with tables, perhaps a few views, and some foreign key constraints, and you want to create a model for this database. Solution Let’s say that you have database describing poets and their poetry. Your relational database might look something like the diagram in Figure 2-7. From this database diagram, you can see that a poet can be the author of one or more poems, and each poem can be categorized by its meter, which is the basic pattern of a poem’s verse. It’s not shown in this diagram, but our database also has a view that joins the tables together so that we can more easily enumerate each poet and poem, as well as the poem’s meter. Figure 2-7. A simple database for poets and their poetry
  • 25. Chapter 2 ■ Entity Data Modeling Fundamentals 19 To import the view, tables, and relationships into a model, do the following: 1. Right-click your project, and select Add ➤ New Item. 2. From the Visual C# Items Data templates, select ADO.NET Entity Data Model. 3. Select Generate from database to create the model from our existing tables. Click Next. 4. Either choose an existing connection to your database or create a new connection. If you are creating a new connection, you will need to select your database server, your authentication method (Windows or SQL Server), and the database. Once you have selected these, it’s a good idea to click Test Connection to be sure that the connection is ready to go. Once you have tested the connection, click Next. The next dialog box shows all of the tables, views, and stored procedures in the database. Check the items you want to include in the model. We want to select all of the tables (Meter, Poem, and Poet). We also want to select the view (vwLibrary). For now, leave the two check boxes for pluralizing and including foreign key columns selected. We will discuss them further momentarily. Figure 2-8 shows the things we’ve selected. When you click Finish, the wizard will create a new model with our three tables and the view. The wizard will also read the foreign key constraints from the database and infer a one-to-many relationship between Poet and Poem(s) as well as a one-to-many relationship between Meter and Poem(s). Figure 2-8. Selecting the tables and view to include in our model. Leave the Pluralize or singularize generated object names and Include Foreign Key Columns in the model checked
  • 26. Chapter 2 ■ Entity Data Modeling Fundamentals 20 Figure 2-9 shows the new model created for us by including the Poet, Poem, and Meter tables as well as the vwLibrary view. You now have a model that you can use in your code. Note that the vwLibrary entity is based on the vwLibrary view in our database. In most databases, views are read-only objects: inserts, deletes, and updates are typically not supported at the database layer. This is also the case with Entity Framework. Entity Framework considers views read only. You can get around this by mapping stored procedures for the create, update, and delete actions for view-based entities. We will show you how to do just that in Chapter 6. How It Works Let’s look at the model created for us by the importing process. Notice that the entities have scalar properties and navigation properties. The scalar properties map to the columns in the tables of the database, while the navigation properties are derived from the relationships between the tables. In our database diagram, a poem has a meter and a poet (the author). These correspond to the Meter and Poet navigation properties. If we have an instance of a Poem entity, the Poet navigation property holds an instance of a Poet entity, while the Meter navigation property holds an instance of a Meter entity. A poet can be the author of any number of poems. The Poems navigation property contains a collection of instances of the Poem entity. This collection can be empty, of course, for those poets that have yet to write any poetry. For the Meter entity, the Poems navigation property is also a collection. For this navigation property, the collection holds instances of Poems that have the given meter. SQL Server does not support relationships defined on views, and our model reflects this with an empty set of navigation properties on the vwLibrary entity. Notice that the Import Wizard was smart enough to pluralize the navigation properties that contained collections. If you right-click the entities and look at their properties, you will notice that the entity set names for each of the Figure 2-9. Our completed model
  • 27. Chapter 2 ■ Entity Data Modeling Fundamentals 21 entities are also property pluralized. For example, the entity set name for the Poem entity is Poems. This automatic pluralization happened because we left the Pluralize or singularize generated object names option checked. The Include Foreign Key Columns in the model option also caused the foreign keys to be included in the model. Although it may seem a little unnecessary to have both foreign keys and navigation properties, we’ll see in many of the following recipes that having direct access to the foreign keys can be useful. The code in Listing 2-2 demonstrates how to create instances of Poet, Poem, and Meter entities in our model and how to save these entities to our database. The code also shows you how to query the model to retrieve the poets and poems from the database. Listing 2-2. Inserting into and Querying Our Model using (var context = new EF6RecipesContext()) { var poet = new Poet { FirstName = John, LastName = Milton }; var poem = new Poem { Title = Paradise Lost }; var meter = new Meter { MeterName = Iambic Pentameter }; poem.Meter = meter; poem.Poet = poet; context.Poems.Add(poem); poem = new Poem { Title = Paradise Regained }; poem.Meter = meter; poem.Poet = poet; context.Poems.Add(poem); poet = new Poet { FirstName = Lewis, LastName = Carroll }; poem = new Poem { Title = The Hunting of the Shark }; meter = new Meter { MeterName = Anapestic Tetrameter }; poem.Meter = meter; poem.Poet = poet; context.Poems.Add(poem); poet = new Poet { FirstName = Lord, LastName = Byron }; poem = new Poem { Title = Don Juan }; poem.Meter = meter; poem.Poet = poet; context.Poems.Add(poem); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { var poets = context.Poets; foreach (var poet in poets) { Console.WriteLine({0} {1}, poet.FirstName, poet.LastName); foreach (var poem in poet.Poems) { Console.WriteLine(t{0} ({1}), poem.Title, poem.Meter.MeterName); } } }
  • 28. Chapter 2 ■ Entity Data Modeling Fundamentals 22 // using our vwLibrary view using (var context = new EF6RecipesContext()) { var items = context.vwLibraries; foreach (var item in items) { Console.WriteLine({0} {1}, item.FirstName, item.LastName); Console.WriteLine(t{0} ({1}), item.Title, item.MeterName); } } In the first block of code in Listing 2-2, we create instances of the Poet, Poem, and Meter entity types for the poet John Milton, his poem “Paradise Lost,” and the meter for the poem, which in this case is Iambic Pentameter. Once we have created the instances of the entity types, we set the poem’s Meter property to the meter instance and the poem’s Poet property to the poet instance. Using the same approach, we build up the other entities relating each poem to its meter and poet. Once we have everything in place, we call SaveChanges()to generate and execute the appropriate SQL statements to insert the rows into the underlying database. The output from the code in Listing 2-2 is as follows: Lord Byron Don Juan (Anapestic Tetrameter) Lewis Carroll The Hunting of the Shark (Anapestic Tetrameter) John Milton Paradise Regained (Iambic Pentameter) Paradise Lost (Iambic Pentameter) Lewis Carroll The Hunting of the Shark (Anapestic Tetrameter) Lord Byron Don Juan (Anapestic Tetrameter) John Milton Paradise Regained (Iambic Pentameter) John Milton Paradise Lost (Iambic Pentameter) In the code, we start by creating and initializing instances of the poet, poem, and meter for the first of John Milton’s poems. Once we have these in place, we set the poem’s Meter navigation property and the poem’s Poet navigation property to the instances of poem and meter. Now that we have the poem instance completed, we add it using the Add() method. Entity Framework does all of the remaining work of adding the poem to the Poems collection on the poet instance and adding the poem to the Poems collection on the meter instance. The rest of the setup follows the same pattern. To shorten the code, we reuse variables and instances where we can. Once we have all of the objects created and all the navigation properties initialized, we have completed the object graph. Entity Framework keeps track of the changes we’ve made to build the object graph. These changes are tracked in the database context. Our context variable contains an instance of the database context (it’s of type DbContext), and it is what we used to build the object graph. To send these changes to the database, we call the SaveChanges() method.
  • 29. Chapter 2 ■ Entity Data Modeling Fundamentals 23 To query our model and, of course, verify that we did indeed save everything to the database, we grab a fresh instance of the object context and query it using LINQ to Entities. We could have reused the same instance of the database context, but then we know it has the object graph and any subsequent queries we run against it won’t flow through to the database because the graph is already in memory. Using LINQ to Entities, we query for all of the poets, and for each poet we print out the poet’s name and the details for each of their poems. The code is pretty simple, but it does use a couple of nested for loops. The last block of code uses the vwLibrary entity. This entity is based on our vwLibrary view. This view joins the tables together to flatten things out a bit and provide a cleaner perspective. When we query for each poet against the vwLibraries entity set, we can get by with just one for loop. The output is a little different because we repeat the poet’s name for each poem. There is one last thing to note in this example. We didn’t insert the poets, poems, and meters using the vwLibrary entity because views are always read-only in most database systems. In Entity Framework, we can’t insert (or update, or delete) entities that are based on views. Of course, we’ll show you exactly how to overcome this little challenge in many of the recipes in this book! 2-3. Modeling a Many-to-Many Relationship with No Payload Problem You have a couple of tables in an existing database that are related to each other via a link or junction table. The link table contains just the foreign keys used to link the two tables together into a many-to-many relationship. You want to import these tables to model this many-to-many relationship. Solution Let’s say that your database tables look something like the database diagram in Figure 2-10. To create a model and import these tables and relationships, do the following: 1. Add a new model to your project by right-clicking your project and selecting Add ➤ New Item. Choose ADO.NET Entity Data Model from the Visual C# Items Data templates. 2. Select Generate from database. Click Next. 3. Use the wizard to select an existing connection to your database, or create a new connection. 4. From the Choose Your Database Object dialog box, select the tables Album, LinkTable, and Artist. Leave the Pluralize and Foreign Key options checked. Click Finish. The wizard will create the model shown in Figure 2-11. Figure 2-10. Artists and albums in a many-to-many relationship
  • 30. Chapter 2 ■ Entity Data Modeling Fundamentals 24 The many-to-many relationship between Album and Artist is represented by a line with the * character on both ends. Because an Album can have many Artists, and an Artist can responsible for many Albums, each of these navigation properties is of type EntityCollection. How It Works In Figure 2-11, an artist can be related to many albums, whereas an album can be the work of many artists. Notice that the link table from Figure 2-10 is not represented as an entity in our model. Because our link table has no scalar properties (that is, it has no payload), Entity Framework assumes that its sole purpose is to create the association between Album and Artist. If the link table had scalar properties, Entity Framework would have created a very different model, as we will see in the next recipe. The code in Listing 2-3 demonstrates how to insert new albums and artists into our model and how to query our model for both artists and their albums and albums with their artists. Listing 2-3. Inserting and Querying Our Artists and Albums Model Through the Many-to-Many Association using (var context = new EF6RecipesContext()) { // add an artist with two albums var artist = new Artist { FirstName = Alan, LastName = Jackson }; var album1 = new Album { AlbumName = Drive }; var album2 = new Album { AlbumName = Live at Texas Stadium }; artist.Albums.Add(album1); artist.Albums.Add(album2); context.Artists.Add(artist); // add an album for two artists var artist1 = new Artist { FirstName = Tobby, LastName = Keith }; var artist2 = new Artist { FirstName = Merle, LastName = Haggard }; var album = new Album { AlbumName = Honkytonk University }; artist1.Albums.Add(album); artist2.Albums.Add(album); context.Albums.Add(album); context.SaveChanges(); } Figure 2-11. The model with a many-to-many relationship between our tables
  • 31. Chapter 2 ■ Entity Data Modeling Fundamentals 25 using (var context = new EF6RecipesContext()) { Console.WriteLine(Artists and their albums...); var artists = context.Artists; foreach (var artist in artists) { Console.WriteLine({0} {1}, artist.FirstName, artist.LastName); foreach (var album in artist.Albums) { Console.WriteLine(t{0}, album.AlbumName); } } Console.WriteLine(nAlbums and their artists...); var albums = context.Albums; foreach (var album in albums) { Console.WriteLine({0}, album.AlbumName); foreach (var artist in album.Artists) { Console.WriteLine(t{0} {1}, artist.FirstName, artist.LastName); } } } The output from the code in Listing 2-3 looks like the following: Artists and their albums... Alan Jackson Drive Live at Texas Stadium Tobby Keith Honkytonk University Merle Haggard Honkytonk University Albums and their artists... Drive Alan Jackson Live at Texas Stadium Alan Jackson Honkytonk University Tobby Keith Merle Haggard After getting an instance of our database context, we create and initialize an instance of an Artist entity type and a couple of instances of the Album entity type. We add the albums to the artist and then add the artist to the Database Context. Next we create and initialize a couple of instances of the Artist entity type and an instance of the Album entity type. Because the two artists collaborated on the album, we add the album to both artists’ Albums navigation property (which is of type EntityCollection). Adding the album to the Database Context causes the artists to get added as well.
  • 32. Chapter 2 ■ Entity Data Modeling Fundamentals 26 Now that the completed object graph is part of the database context, the only thing left to do is to use SaveChanges() to save the whole thing to the database. When we query the database in a brand-new Database Context, we grab the artists and display their albums. Then we grab the albums and print the artists that created the albums. Notice that we never refer to the underlying LinkTable from Figure 2-10. In fact, this table is not even represented in our model as an entity. The LinkTable is represented in the many-to-many association, which we access via the Artists and Albums navigation properties. 2-4. Modeling a Many-to-Many Relationship with a Payload Problem You have a many-to-many relationship in which the link table contains some payload data (any additional columns beyond the foreign keys), and you want to create a model that represents the many-to-many relationship as two one-to-many associations. Solution Entity Framework does not support associations with properties, so creating a model like the one in the previous recipe won’t work. As we saw in the previous recipe, if the link table in a many-to-many relationship contains just the foreign keys for the relationship, Entity Framework will surface the link table as an association and not as an entity type. If the link table contains additional information, Entity Framework will create a separate entity type to represent the link table. The resulting model will contain two one-to-many associations with an entity type representing the underlying link table. Suppose we have the tables and relationships shown in Figure 2-12. Figure 2-12. A many-to-many relationship with payload An Order can have many Items. An Item can be on many Orders. Additionally, we have a Count property connected to each instance of the Order, Item relationship. This Count property is referred to as a payload. To create a model and import these tables and relationships into the model, do the following: 1. Add a new model to your project by right-clicking your project and selecting Add ➤ New Item. Choose ADO.NET Entity Data Model from the Visual C# Data templates. 2. Select Generate from database. Click Next. 3. Use the wizard to select an existing connection to your database or create a new connection. 4. From the Choose Your Database Object dialog box, select the tables Order, OrderItem, and Item. Leave the Pluralize and Foreign Key options checked. Click Finish. The wizard will create the model in Figure 2-13.
  • 33. Chapter 2 ■ Entity Data Modeling Fundamentals 27 How It Works As we saw in the previous recipe, for a many-to-many relationship with no payload, the model is clean and simple to navigate. Because Entity Framework does not support the notion of payloads on associations, it surfaces the link table as an entity with two one-to-many associations to the related entities. In this case, the OrderItem table is represented not as an association, but as an entity type with a one-to-many association to Order and a one-to-many association to Item. In the previous recipe, the payload-free link table did not translate into an entity type in the model. Instead, it became part of the many-to-many association. The addition of a payload requires an additional hop through the entity representing the link table to retrieve the related items. This is illustrated in code in Listing 2-4. Listing 2-4. Inserting into and Retrieving from the Model using (var context = new EF6RecipesContext()) { var order = new Order { OrderId = 1, OrderDate = new DateTime(2010, 1, 18) }; var item = new Item { SKU = 1729, Description = Backpack, Price = 29.97M }; var oi = new OrderItem { Order = order, Item = item, Count = 1 }; item = new Item { SKU = 2929, Description = Water Filter, Price = 13.97M }; oi = new OrderItem { Order = order, Item = item, Count = 3 }; item = new Item { SKU = 1847, Description = Camp Stove, Price = 43.99M }; oi = new OrderItem { Order = order, Item = item, Count = 1 }; context.Orders.Add(order); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { foreach (var order in context.Orders) { Console.WriteLine(Order # {0}, ordered on {1}, order.OrderId.ToString(), order.OrderDate.ToShortDateString()); Figure 2-13. Two one-to-many associations from a many-to-many relationship with payload
  • 34. Chapter 2 ■ Entity Data Modeling Fundamentals 28 Console.WriteLine(SKUtDescriptiontQtytPrice); Console.WriteLine(---t-----------t---t-----); foreach (var oi in order.OrderItems) { Console.WriteLine({0}t{1}t{2}t{3}, oi.Item.SKU, oi.Item.Description, oi.Count.ToString(), oi.Item.Price.ToString(C)); } } } The following is the output from the code shown in Listing 2-4. Order # 1, ordered on 1/18/2010 SKU Description Qty Price ---- ----------- --- ------ 1729 Backpack 1 $29.97 1847 Camp Stove 1 $43.99 2929 Water Filter 3 $13.97 After we create an instance of our database context, we create and initialize an Order entity as well as the items and order items for the order. We connect the order with the items by initializing the OrderItem entities with the instances of the Order entity and the Item entity. We use the Add() method to add the order to the context. With the object graph complete and the order added to the context, we update the database with the SaveChanges() method. To retrieve the entities from the database, we create a fresh instance of the context and iterate through the context.Orders collection. For each order (well, we just have one in this example), we print the order detail and we iterate through the entity collection on the OrderItems navigation property. These instances of the OrderItem entity type give us access to the Count scalar property (the payload) directly, and each item on the order via the Item navigation property. Going through the OrderItems entity to get to the items is the “extra” hop that is the cost of having a payload in the link table (OrderItems, in our example) in a many-to-many relationship. Best Practice Unfortunately, a project that starts out with several payload-free many-to-many relationships often ends up with several payload-rich many-to-many relationships. Refactoring a model, especially late in the development cycle, to accommodate payloads in the many-to-many relationships can be tedious. Not only are additional entities introduced, but the queries and navigation patterns through the relationships change as well. Some developers argue that every many-to-many relationship should start off with some payload, typically a synthetic key, so that the inevitable addition of more payload has significantly less impact on the project. So here’s the best practice: If you have a payload-free many-to-many relationship and you think there is some chance that it may change over time to include a payload, start with an extra identity column in the link table. When you import the tables into your model, you will get two one-to-many relationships, which means the code you write and the model you have will be ready for any number of additional payload columns that come along as the project matures. The cost of an additional integer identity column is usually a pretty small price to pay to keep the model more flexible.
  • 35. Chapter 2 ■ Entity Data Modeling Fundamentals 29 2-5. Modeling a Self-Referencing Relationship with a Code-First Approach Problem You have a table that references itself, and you want to model this as an entity with a self-referencing association using a Code-First approach. Solution Let’s say that you have a self-referencing table that’s like the one shown in the database diagram in Figure 2-14. To create a model and import this table and the self-referencing relationship into the model, do the following: 1. Create a new class that inherits from DbContext in your project. 2. Use the code in Listing 2-5 to create the PictureCategory POCO entity. Listing 2-5. Creating the PictureCategory POCO Entity public class PictureCategory { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int CategoryId { get; private set; } public string Name { get; set; } public int? ParentCategoryId { get; private set; } [ForeignKey(ParentCategoryId)] public PictureCategory ParentCategory { get; set; } public ListPictureCategory Subcategories { get; set; } public PictureCategory() { Subcategories = new ListPictureCategory(); } } Figure 2-14. A self-referencing table
  • 36. Chapter 2 ■ Entity Data Modeling Fundamentals 30 3. Add a DbSetPictureCategory auto property to your DbContext subclass. 4. Override the OnModelCreating method in your DbContext class to configure the bidirectional association (ParentCategory and SubCategories), as seen in Listing 2-6. Listing 2-6. Overriding OnModelCreating in DbContext Subclass public class EF6RecipesContext : DbContext { public DbSetPictureCategory PictureCategories { get; set; } public PictureContext() : base(name=EF6CodeFirstRecipesContext) { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.EntityPictureCategory() .HasMany(cat = cat.SubCategories) .WithOptional(cat = cat.ParentCategory); } } How It Works Database relationships are characterized by degree, multiplicity, and direction. Degree is the number of entity types that participate in the relationship. Unary and binary relationships are the most common. Tertiary and n-place relationships are more theoretical than practical. Multiplicity is the number of entity types on each end of the relationship. You have seen the multiplicities 0..1 (zero or 1), 1 (one), and * (many). Finally, the direction is either one-way or bidirectional. The Entity Data Model supports a particular kind of database relationship called an Association Type. An Association Type relationship has either unary or binary degree, multiplicities 0..1, 1, or *, and a direction that is bidirectional. In this example, the degree is unary (just the entity type PictureCategory is involved), the multiplicity is 0..1 and *, and the direction is, of course, bidirectional. As is the case in this example, a self-referencing table often denotes a parent-child relationship, with each parent having many children while each child has just one parent. Because the parent end of the relationship is 0..1 and not 1, it is possible for a child to have no parent. This is just what you want to leverage in representing the root node; that is, the one node that has no parent and is the top of the hierarchy. Listing 2-7 shows how you can recursively enumerate the picture categories starting with the root node, which, of course, is the only node that has no parent.
  • 37. Chapter 2 ■ Entity Data Modeling Fundamentals 31 Listing 2-7. Inserting into Our Model and Recursively Enumerating All of the Instances of the Self-referencing entity static void RunExample() { using (var context = new EF6RecipesContext()) { var louvre = new PictureCategory { Name = Louvre }; var child = new PictureCategory { Name = Egyptian Antiquites }; louvre.Subcategories.Add(child); child = new PictureCategory { Name = Sculptures }; louvre.Subcategories.Add(child); child = new PictureCategory { Name = Paintings }; louvre.Subcategories.Add(child); var paris = new PictureCategory { Name = Paris }; paris.Subcategories.Add(louvre); var vacation = new PictureCategory { Name = Summer Vacation }; vacation.Subcategories.Add(paris); context.PictureCategories.Add(paris); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { var roots = context.PictureCategories.Where(c = c.ParentCategory == null); roots.ForEach(root = Print(root, 0)); } } static void Print(PictureCategory cat, int level) { StringBuilder sb = new StringBuilder(); Console.WriteLine({0}{1}, sb.Append(' ', level).ToString(), cat.Name); cat.Subcategories.ForEach(child = Print(child, level + 1)); } The output of the code in Listing 2-7 shows our root node: Summer Vacation. The first (and only) child is Paris. Paris has Louvre as a child. Finally, at the Louvre, we categorized our pictures by the various collections we visited. Summer Vacation Paris Louvre Egyptian Antiquities Sculptures Paintings Clearly, the code is a little involved. We start by creating and initializing the instances of our entity types. We wire them together in the object graph by adding the PictureCategories to our louvre category. Then we add the louvre category to the paris category. Finally, we add the paris category to our summer vacation category. We build the hierarchy from the bottom up.
  • 38. Chapter 2 ■ Entity Data Modeling Fundamentals 32 Once we do a SaveChanges(), the inserts are all done on the database, and it’s time to query our tables to see whether we’ve actually inserted all of the rows correctly. For the retrieval part, we start by getting the root entity. This is the one that has no parent. In our case, we created a summer vacation entity, but we didn’t make it the child of any other entity. This makes our summer vacation entity the root of the hierarchy. Now, with the root, we call another method we wrote: Print(). The Print() method takes a couple of parameters. The first parameter is an instance of a PictureCategory. The second parameter is a level, or depth, we are at in the hierarchy. With the root category, summer vacation, we’re at the top of the hierarchy, so we pass in 0. The method call looks like Print(root, 0). In the Print() method, we write out the name of the category preceded by a space for each level deep in the hierarchy. One of the Append() methods of the StringBuilder class takes a character and an integer. It creates an instance of StringBuilder with the character appended the number of times specified by the integer parameter. In our call, we send in a space and level, and it returns a string with a space for every level deep that we are in the hierarchy. We use the ToString() method to convert the StringBuilder instance to a string. Now for the recursive part: We iterate through the children and call the Print() method on each child, making sure to increment the level by one. When we run out of children, we simply return. The result is the output shown previously. In Recipe 6-5, we show another approach to this problem using a Common Table Expression in a stored procedure on the store side to iterate through the graph and return a single flattened result set. 2-6. Splitting an Entity Among Multiple Tables Problem You have two or more tables that share the same primary key, and you want to map a single entity to these two tables. Solution Let’s illustrate the problem with the two tables shown in Figure 2-15. Figure 2-15. Two tables, Product and ProductWebInfo, with common primary keys To create a model with a single entity representing these two tables, do the following: 1. Create a new class in your project that inherits from DbContext. 2. Create a Product POCO entity using the code in Listing 2-8.
  • 39. Chapter 2 ■ Entity Data Modeling Fundamentals 33 Listing 2-8. Creating the Product POCO Entity public class Product { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] public int SKU { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string ImageURL { get; set; } } 3. Add an auto-property of type DbSetProduct to your DbContext subclass. 4. Override the OnModelCreating() method of DbContext with the code in Listing 2-9. Listing 2-9. Overriding OnModelCreating in the DbContext Subclass public class EF6RecipesContext : DbContext { public DbSetProduct Products { get; set; } public ProductContext() : base(name=EF6CodeFirstRecipesContext) { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.EntityProduct() .Map(m = { m.Properties(p = new {p.SKU, p.Description, p.Price}); m.ToTable(Product, Chapter2); }) .Map(m = { m.Properties(p = new {p.SKU, p.ImageURL}); m.ToTable(ProductWebInfo, Chapter2); }); } } How It Works It seems all too common in legacy systems to find “extra” information for each row in one table tucked away in another table. Often this happens over time as a database evolves, and no one is willing to break existing code by adding columns to some critical table. The answer is to “graft on” a new table to hold the additional columns. By merging two or more tables into a single entity or, as it is usually perceived, splitting a single entity across two or more tables, we can treat all of the parts as one logical entity. This process is often referred to as vertical splitting.
  • 40. Chapter 2 ■ Entity Data Modeling Fundamentals 34 The downside of vertical splitting is that retrieving each instance of our entity now requires an additional join for each additional table that makes up the entity type. This extra join is shown in Listing 2-10. Listing 2-10. Additional Join Required by Vertical Splitting SELECT [Extent1].[SKU] AS [SKU], [Extent2].[Description] AS [Description], [Extent2].[Price] AS [Price], [Extent1].[ImageURL] AS [ImageURL] FROM [dbo].[ProductWebInfo] AS [Extent1] INNER JOIN [dbo].[Product] AS [Extent2] ON [Extent1].[SKU] = [Extent2].[SKU] Nothing special is required to insert into or retrieve from the Product entity. Listing 2-11 demonstrates working with the vertically split Product entity type. Listing 2-11. Inserting into and Retrieving from Our Model with the Product Entity Type using (var context = new EF6RecipesContext()) { var product = new Product { SKU = 147, Description = Expandable Hydration Pack, Price = 19.97M, ImageURL = /pack147.jpg }; context.Products.Add(product); product = new Product { SKU = 178, Description = Rugged Ranger Duffel Bag, Price = 39.97M, ImageURL = /pack178.jpg }; context.Products.Add(product); product = new Product { SKU = 186, Description = Range Field Pack, Price = 98.97M, ImageURL = /noimage.jp }; context.Products.Add(product); product = new Product { SKU = 202, Description = Small Deployment Back Pack, Price = 29.97M, ImageURL = /pack202.jpg }; context.Products.Add(product); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { foreach (var p in context.Products) { Console.WriteLine({0} {1} {2} {3}, p.SKU, p.Description, p.Price.ToString(C), p.ImageURL); } }
  • 41. Chapter 2 ■ Entity Data Modeling Fundamentals 35 The code in Listing 2-7 produces the following results: 147 Expandable Hydration Pack $19.97 /pack147.jpg 178 Rugged Ranger Duffel Bag $39.97 /pack178.jpg 186 Range Field Pack $98.97 /noimage.jpg 202 Small Deployment Back Pack $29.97 /pack202.jpg 2-7. Splitting a Table Among Multiple Entities Problem You have a table with some frequently used fields and a few large, but rarely needed fields. For performance reasons, you want to avoid needlessly loading these expensive fields on every query. You want to split the table across two or more entities. Solution Let’s say that you have a table like the one shown in Figure 2-16, which holds information about photographs as well as the bits for both the thumbnail and full-resolution image of the photograph. Figure 2-16. A Photograph table with a field holding the binary large object (blob) representing the data for the image To create an entity type that contains the reasonably low-cost and frequently used columns, as well as an entity type containing the high-cost but rarely used HighResolutionBits column, do the following: 1. Create a new class in your project that inherits from DbContext. 2. Create a Photograph POCO entity class using the code in Listing 2-12.
  • 42. Chapter 2 ■ Entity Data Modeling Fundamentals 36 Listing 2-12. Creating the Photograph POCO Entity public class Photograph { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int PhotoId { get; set; } public string Title { get; set; } public byte[] ThumbnailBits { get; set; } [ForeignKey(PhotoId)] public virtual PhotographFullImage PhotographFullImage { get; set; } } 3. Create a PhotographFullImage POCO entity class using the code in Listing 2-13. Listing 2-13. Creating the PhotographFullImage POCO Entity public class PhotographFullImage { [Key] public int PhotoId { get; set; } public byte[] HighResolutionBits { get; set; } [ForeignKey(PhotoId)] public virtual Photograph Photograph { get; set; } } 4. Add an auto-property of type DbSetPhotograph to your DbContext subclass. 5. Add another auto-property type of DbSetPhotographFullImage to your DbContext subclass. 6. Override the OnModelCreating() method of the DbContext class, as shown in Listing 2-14. Listing 2-14. Overriding the OnModelCreating Method of DbContext protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.EntityPhotograph() .HasRequired(p = p.PhotographFullImage) .WithRequiredPrincipal(p = p.Photograph); modelBuilder.EntityPhotograph().ToTable(Photograph, Chapter2); modelBuilder.EntityPhotographFullImage().ToTable(Photograph, Chapter2); }
  • 43. Chapter 2 ■ Entity Data Modeling Fundamentals 37 How It Works Entity Framework does not directly support the notion of lazy loading of individual entity properties. To get the effect of lazy loading expensive properties, we exploit Entity Framework’s support for lazy loading of associated entities. We created a new entity type to hold the expensive full image property and created a one-to-one association between our Photograph entity type and the new PhotographFullImage entity type. We added a referential constraint on the conceptual layer that, much like a database referential constraint, tells Entity Framework that a PhotographFullImage can’t exist without a Photograph. Due to the referential constraint, there are a couple of things to note about our model. If we have a newly created PhotographFullImage, an instance of Photograph must exist in the object context or the data source prior to calling SaveChanges(). Also, if we delete a photograph, the associated PhotographFullImage is also deleted. This is just like cascading deletes in database referential constraints. The code in Listing 2-15 demonstrates inserting and retrieving from our model. Listing 2-15. Inserting into and Lazy Loading Expensive Fields byte[] thumbBits = new byte[100]; byte[] fullBits = new byte[2000]; using (var context = new EF6RecipesContext()) { var photo = new Photograph { Title = My Dog, ThumbnailBits = thumbBits }; var fullImage = new PhotographFullImage { HighResolutionBits = fullBits }; photo.PhotographFullImage = fullImage; context.Photographs.Add(photo); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { foreach (var photo in context.Photographs) { Console.WriteLine(Photo: {0}, ThumbnailSize {1} bytes, photo.Title, photo.ThumbnailBits.Length); // explicitly load the expensive entity, PhotographFullImagecontext.Entry(photo).Reference(p = p.PhotographFullImage).Load(); Console.WriteLine(Full Image Size: {0} bytes, photo.PhotographFullImage.HighResolutionBits.Length); } } The output from Listing 2-15 is as follows: Photo: My Dog, Thumbnail Size: 100 bytes Full Image Size: 2000 bytes The code in Listing 2-15 creates and initializes instances of the Photograph and PhotographFullImage entities, adds them to the object context, and calls SaveChanges().
  • 44. Chapter 2 ■ Entity Data Modeling Fundamentals 38 On the query side, we retrieve each of the photographs from the database, print some information about the photograph, and then explicitly load the associated PhotographFullImage entity. Notice that we did not change the default context option to turn off lazy loading. This puts the burden on us to load related entities explicitly. This is just what we want. We could have chosen not to load the associated instances of PhotographFullImage, and if we were iterating through hundreds or thousands of photographs, this would have saved us an awful lot of cycles and bandwidth. 2-8. Modeling Table per Type Inheritance Problem You have some tables that contain additional information about a common table, and you want to model this using table per type inheritance. Solution Suppose that you have two tables that are closely related to a common table, as shown in Figure 2-17. The Business table is on the 1 side of a 1:0..1 relationship with the eCommerce and the Retail tables. The key feature here is that the eCommerce and Retail tables extend information about a business represented in the Business table. The tables Retail and eCommerce are related to the Business table, which holds a few properties that we would naturally associate with any business. To model table per type inheritance such that entities Retail and eCommerce inherit from the Business base entity type, perform the following steps: 1. Create a new class in your project that inherits from DbContext. 2. Create a Business POCO entity class using the code in Listing 2-16. Figure 2-17. Closely related tables ripe for inheritance
  • 45. Chapter 2 ■ Entity Data Modeling Fundamentals 39 Listing 2-16. Creating the Business POCO Entity Class [Table(Business, Schema = Chapter2)] public class Business { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int BusinessId { get; protected set; } public string Name { get; set; } public string LicenseNumber { get; set; } } 3. Create an eCommerce POCO entity class that inherits from the Business class using the code in Listing 2-17. Listing 2-17. Creating the eCommerce POCO Entity Class [Table(eCommerce, Schema = Chapter2)] public class eCommerce : Business { public string URL { get; set; } } 4. Create a Retail POCO entity class that inherits from the Business class using the code in Listing 2-18. Listing 2-18. Creating the Retail POCO Entity Class [Table(Retail, Schema = Chapter2)] public class Retail : Business { public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string ZIPCode { get; set; } } 5. Add an auto-property of type DbSetBusiness to your DbContext subclass. How It Works Both the Retail and the eCommerce tables are on the 0..1 side of a 1:0..1 relationship with the Business table. This means that we could have a business with no additional information or a business with additional Retail or eCommerce information. In object-oriented programming terms, we have a base type, Business, with two derived types, Retail and eCommerce. Because of the 1:0..1 relationship, we cannot have a row in the Retail or eCommerce tables without a corresponding row in the Business table. In object-oriented terms, an instance of a derived type has the properties of the base type. This concept of a derived type extending the properties of a base type is a key feature of inheritance. In table per type (often abbreviated TPT) inheritance, each of the derived types is represented in separate tables.
  • 46. Chapter 2 ■ Entity Data Modeling Fundamentals 40 Listing 2-19 demonstrates inserting and retrieving from our model. Listing 2-19. Inserting and Retrieving Entities in TPT Inheritance using (var context = new EF6RecipesContext()) { var business = new Business { Name = Corner Dry Cleaning, LicenseNumber = 100x1 }; context.Businesses.Add(business); var retail = new Retail { Name = Shop and Save, LicenseNumber = 200C, Address = 101 Main, City = Anytown, State = TX, ZIPCode = 76106 }; context.Businesses.Add(retail); var web = new eCommerce { Name = BuyNow.com, LicenseNumber = 300AB, URL = www.buynow.com }; context.Businesses.Add(web); context.SaveChanges(); } using (var context = new EF6RecipesContext()) { Console.WriteLine(n--- All Businesses ---); foreach (var b in context.Businesses) { Console.WriteLine({0} (#{1}), b.Name, b.LicenseNumber); } Console.WriteLine(n--- Retail Businesses ---); foreach (var r in context.Businesses.OfTypeRetail()) { Console.WriteLine({0} (#{1}), r.Name, r.LicenseNumber); Console.WriteLine({0}, r.Address); Console.WriteLine({0}, {1} {2}, r.City, r.State, r.ZIPCode); } Console.WriteLine(n--- eCommerce Businesses ---); foreach (var e in context.Businesses.OfTypeeCommerce()) { Console.WriteLine({0} (#{1}), e.Name, e.LicenseNumber); Console.WriteLine(Online address is: {0}, e.URL); } } The code in Listing 2-19 creates and initializes instances of the Business entity type and the two derived types. To add these to the Database Context, we use the Add() method exposed on the Business entity set in the context. On the query side, to access all of the businesses, we iterate through the Businesses entity set. For the derived types, we use the OfType() method specifying the derived type to filter the Business entity set.
  • 47. Chapter 2 ■ Entity Data Modeling Fundamentals 41 The output of Listing 2-19 looks like the following: --- All Businesses --- Corner Dry Cleaning (#100X1) Shop and Save (#200C) BuyNow.com (#300AB) --- Retail Businesses --- Shop and Save (#200C) 101 Main Anytown, TX 76106 ---- eCommerce Businesses --- BuyNow.com (#300AB) Online address is: www.buynow.com Table per type is one of three inheritance models supported by Entity Framework. The other two are Table per Hierarchy (discussed in this chapter) and Table per Concrete Type (see Chapter 15). Table per type inheritance provides a lot of database flexibility because we can easily add tables as new derived types find their way into our model as an application develops. However, each derived type involves additional joins that can reduce performance. In real-world applications, we have seen significant performance problems with TPT when many derived types are modeled. Table per hierarchy, as you will see in Recipe 2-10, stores the entire hierarchy in a single table. This eliminates the joins of TPT and thereby provides better performance, but at the cost of some flexibility. Table per concrete type is supported by the Entity Framework runtime, but not by the designer. Table per Concrete Type has some important applications, as we will see in Chapter 15. 2-9. Using Conditions to Filter an ObjectSet Problem You want to create a permanent filter on an entity type so that it maps to a subset of the rows in a table. Solution Let’s say that you have a table holding account information, as shown in the database diagram in Figure 2-18. The table has a DeletedOn nullable column that holds the date and time the account was deleted. If the account is still active, the DeletedOn column is null. We want our Account entity to represent only active accounts; that is, an account without a DeletedOn value.
  • 48. Chapter 2 ■ Entity Data Modeling Fundamentals 42 To model this table so that only active accounts are used to populate the Account entity type, do the following: 1. Add a new model to your project by right-clicking your project and selecting Add ➤ New Item. Choose ADO.NET Entity Data Model from the Visual C# Data templates. 2. Select Generate from database. Click Next. 3. Use the wizard to select an existing connection to your database, or create a new connection. 4. From the Choose Your Database Object dialog box, select the Account table. Leave the Pluralize and Foreign Key options checked. Click Finish. 5. Click the Account entity to view the Mapping Details window. If the Mapping Details window is not visible, show it by selecting View ➤ Other Windows ➤ Entity Data Model Mapping Details. Click Add a Condition, and select the DeletedOn column. In the Operator column, select Is, and in the Value/Property column, select Null. This creates a mapping condition when the DeletedOn column is Is Null (see Figure 2-19). Figure 2-18. Account table with DeletedOn DateTime column
  • 49. Other documents randomly have different content
  • 50. to know these relations of his a little better. Hubert was a good chap, and so was Uncle Joe. He had not properly understood them until to- day. And now that he knew her story, he would like to know something more of Eleanor. There was something fine about her, and the thought of that dark, solemn little chit in the hall made him feel oddly tender towards her. The darkness had fallen, and the clouds had reassembled in tremendous masses that were moving with strange swiftness across the sky. Leaning back and looking upward it was interesting to contrast the windless quiet of the garden in which they were sitting with the evidences of the tumult above. It's beginning to rain, his uncle suddenly exclaimed, breaking a long silence. We'd better go. Arthur was prepared for some display of temper on the part of Miss Kenyon when he and his uncle entered the drawing-room, and was disappointed to find that she displayed her habitual air of cold reserve. He was a trifle nervous and apprehensive now, about this projected embassy of his, and would have been glad to have been stiffened by some show of active opposition. Miss Kenyon had, he thought, something of the same awful detachment that her father exhibited towards every-day affairs. All the older members of the party were there. Turner had a novel in his hand, the three women were busy with their usual fancy-work, but to-night they had drawn together in a group by one of the windows, with an effect of being in conference. Joe Kenyon's action in pulling up a chair and joining the group held a faint suggestion of bravado. He had the uneasy air of a man coming to a confession of his own weakness. Arthur preferred to stand, leaning against the jamb of the window. It gave him a physical sense of superiority to look down upon his antagonist.
  • 51. Joe Kenyon plunged at once into what Arthur judged to be relatively a side issue. Arthur and I have been talking about Hubert's engagement, he said. Hubert had been telling him all about it this afternoon; and Arthur has suggested that he should say something to my father. If he had deliberately intended an effect of surprise he had attained his object. They were undoubtedly startled by this announcement, and not less obviously puzzled. It was not, however, Arthur's part in the affair that seemed to perplex them. None of them looked up at him, they were all staring at Joe Kenyon, with an expression that seemed, Arthur thought, to be seeking for a private sign. But so far as he could see, none was given. Joe Kenyon was leaning back in his chair and wiping his forehead. This rain ought to cool the air a bit, he interjected in an undertone. Beastly hot in here. Very friendly of Arthur, Turner commented, turning slightly towards the young man as he spoke. No reason, after all, why he should bother himself about our affairs. I suppose he understands ... his wife began, and then stopped abruptly. She was still looking anxiously at her brother as if inviting further confidences. Joe Kenyon nodded. Oh, of course, of course, he said. Hubert told him all about it this afternoon. About what, Joe? Miss Kenyon put in, speaking for the first time. She gave him no indication of perturbation or anxiousness, but she was reading her brother's face as if she sought some evidence of his secret motive. Well, about the engagement, and having no money and so on, Joe Kenyon rather desperately explained. No money? his sister returned, with a lift of her eyebrows. What do you mean, by having no money?
  • 52. Well, Hubert hasn't any, not of his own, her brother replied. And he was saying, I gather, that he would like—well—a change of air if he were married. About enough of us here, without him, perhaps. That sort of thing. And Arthur very generously offered through me to lend him a couple of hundred pounds if he wanted it. Whether or not he had intended to create a diversion by this further announcement, he had certainly achieved that object. Turner gave an exclamation of surprise, but it was Mrs Kenyon who answered. Oh, but we couldn't possibly accept that, in an agitated voice; and Arthur, looking down, saw that her hands were trembling. She was, he realised then, by far the most nervous of the five, and he recognised in her at that moment a strong likeness to his own mother. She, too, had been a timid woman, apprehensive not only of danger, but also of change. Miss Kenyon had let her work fall in her lap, and was sitting, plunged, apparently, in a fit of deep abstraction. No, no, of course not, Joe Kenyon replied. I have already refused that. On what grounds? Miss Kenyon put in sharply. Er—I don't think—I suggested, Esther, that Hubert would be—well, rather lost if he were to find himself in a new country with a wife to support on a capital of £200. Miss Kenyon gave a short impatient sniff, and turned to Arthur. A little strange, isn't it, she asked, for you to offer to finance us? Only Hubert, you know, Arthur explained. Hubert has a father and mother alive, to say nothing of uncles and aunts, she returned. I don't know why he should need help from a comparative stranger.
  • 53. He seemed to need it, Arthur said dryly, or I shouldn't have made the offer. Miss Kenyon shrugged her shoulders and turned back to her brother. Are we to understand, Joe, she said, that Arthur Woodroffe knows all about us now? Have you told him everything? Damn it, Esther, what do you mean by everything? Joe Kenyon exploded defensively. I—it seems to me—Hubert had pretty well told him all that mattered, before I said a word. I told him about Jim, if that's what you mean? Miss Kenyon began to drum her fingers on the arm of her chair. And what good do you expect to do to yourself or anybody else by speaking to my father about Hubert's engagement? she asked Arthur. Turner leant back in his chair and crossed his legs. Precisely, that's the real point, he agreed. Well, naturally, I hope to persuade Mr Kenyon to sanction the engagement, Arthur said. Why? snapped Miss Kenyon. Friendship for Hubert, Arthur said. I wasn't aware that you and he were such great friends, was Miss Kenyon's criticism of that explanation. Oh, well, pretty fair, Arthur compromised. Anyhow, I'll be glad to help him if I can. I can't imagine that anything you could say to my father would carry the least weight, Miss Kenyon said dryly. Perhaps not, Arthur agreed. No harm in trying, though, is there? I think that's quite true, you know, Esther, Mrs Kenyon put in, and it would be rather a relief if—that is, I hope, for Hubert's sake at all
  • 54. events, something can be done to smooth things over. Miss Kenyon turned from her sister-in-law with a slight suggestion of contempt. Do you know this girl, Dorothy Martin? she asked, looking at her brother. Slightly, he said. Met her twice, I think. Seemed a jolly girl, I thought. Full of life. Quite a nice girl, his wife put in eagerly. Oh! you've met her too, have you? Miss Kenyon commented coldly. At the Club House. Hubert took me up there to tea, the day before yesterday, on purpose to introduce me, Mrs Kenyon explained, with a pathetic air of apology. Arthur had drawn many false inferences about the affairs at Hartling, but it was quite clear to him now that although there might, as his uncle had said, be some tacit agreement as to the Kenyons' attitude toward the head of the house, Miss Kenyon had certainly not been given any confidences concerning Hubert's engagement. She has no money of her own, I suppose? was the next question. Joe Kenyon and his wife looked at each other rather helplessly, and it seemed that no further answer was needed, for Miss Kenyon at once continued, Folly, absurd folly, and you know it. If Arthur Woodroffe likes to make a fool of himself, he can. What he does or does not do is neither here nor there. But I shall have no hand in it, and any influence I have with my father.... She had risen to her feet as she spoke, and now stood with her hands clenched, an erect and dominating figure. She was over sixty, but she was still a handsome woman, full of vitality and energy; and at that moment Arthur could not but concede her a grudging measure of admiration. He felt as if he had seen her fully awake for the first time. Her rather pale blue eyes were suddenly keen and alert, and there was an air of mastery about her that reminded him
  • 55. of her father. By the side of her, Mrs Turner and her brother with their sandy-gray hair and their tendency to an untidy corpulence, seemed to belong to another race. Esther, if the head of the house was to be taken as the standard, was the only true Kenyon of the second generation, unless Eleanor's father, the errant, independent James, had been of his sister's breed? Had he, perhaps, had his sister's hands also; those white, strong managing hands that were now so threateningly clenched? She stood there for a moment, dominating them all, while she allowed the threat of her unfinished sentence to take effect; then she turned and left the room with a quiet dignity that was in itself a menace. Nevertheless, Arthur at least had not been intimidated by her outburst, and her contemptuous reference to himself had provided him with the very stimulant he desired. Moreover, he had now a fierce desire to humiliate his handsome opponent, a desire that arose from a new source. He had seen her as a woman for the first time, and he was aware in himself of a hitherto unrealised impulse to cruelty. He wanted to break and dominate that proud, erect figure. However sneeringly she had challenged him, and in the zest of his unsatisfied youth, he longed to conquer her, although his victory could be but the barren victory of the intellect. He took the seat Miss Kenyon had just vacated with a pleasant sense of mastery. He felt that he could do anything he liked with the other four. They were all of them looking, just then, so completely cowed and depressed. Joe Kenyon and his sister were crumpled into their chairs, with an air of rather absurd dejection. Mrs Kenyon had resumed her fancy work and was bending over it in an attitude that suggested the possibility of hidden tears; and Turner, nervously twisting his exquisitely neat little moustache, was staring thoughtfully at his own reflection in the darkened window. I don't see why we shouldn't help Hubert, all the same, Arthur tried, by way of making a beginning.
  • 56. Little Turner withdrew his gaze from the window and regarded the intrepid youth with an expression of half-amused pity. You don't know, was his only comment. Well, I think I do, to a certain extent, Arthur said boldly. Uncle Joe told me a good many things to-night, one way and another. More than he cared to admit, perhaps, before Miss Kenyon. He had made a deliberate bid for inclusion into their secret counsels by that last sentence, and he had at least succeeded in stimulating their interest. Oh, well, well, his uncle said, sitting up with an effect of reinflation, perhaps I did. Esther's got a queer temper, now and then. And possibly I told you more than was altogether discreet. He looked at his brother-in-law as he added, I'll admit to being a bit down in the mouth about the whole affair. But do you really think, Mrs Kenyon began unhopefully, that it would be any good for you to come into the affair at all? Well, I'm perfectly free, you know, Arthur said, and instantly realised that he had said the forbidden thing. They could not bear that admission of bondage in a full company. Can't see that that's anything to do with it, Turner replied. We're all free enough, so far as that goes. Point is, whether your interference is advisable; whether you might not put Mr Kenyon's back up and make things a hundred times worse for Hubert. Arthur chose to overlook the snub. Well, I don't see that it could do any harm, he said. He felt pleasantly young and capable among those four old people; he believed that they were too inert to oppose him, that they would accept any leader capable of taking the initiative. Anything I did, he continued, would only react on me, and I—don't care. Uncle Joe has warned me that Mr Kenyon may sling me out of the house at an hour's notice, but I'm perfectly willing to take that risk.
  • 57. No one answered him. For the second time in two minutes he had all too clearly displayed their weakness with his youthful boast of freedom, and this time they had no defence but to ignore him. For a few seconds there was a painful, uneasy silence, and then Turner looked at Mrs Kenyon and said, in a confidential tone,— What does Eleanor say about it all? I suppose you've asked her advice? She thinks he'll be against it, Mrs Kenyon said timidly. But nothing has been said to him as yet. She—she would like Hubert to go away —but I can't see how—even if we accepted.... She glanced at Arthur as she concluded. Oh, well, Turner replied, standing up, we'll have to leave it at that presumably. No good in our interfering, obviously. And he looked at his wife, who began to fumble her work into an untidy bundle, preparatory to getting to her feet. With our own trouble hanging over us, she remarked allusively, and added, What's going to happen to poor Ken, I don't know. He's determined that he won't come to live here. They were all standing now, saying good-night, but Joe Kenyon lagged behind with Arthur as they trailed across the spaces of the drawing-room. I'm afraid it's no good, you know, he murmured, very generous of you to make the offer, all the same. When he was alone in his own delightful bedroom, Arthur stood at the open window, listening to the sound of the rain and inhaling the welcome scents of the grateful earth. Already his mood of resentment against these four impotent old people had passed. They had snubbed and checked him, given him to understand that though he might, indeed, know something of the facts of their position, he knew nothing of the spirit. But he could not cherish anger against them, nor even contempt. They had been in shackles too long; he
  • 58. could not reasonably expect them to enter with him into any kind of conspiracy against the old man. They were so helpless, so completely dependent upon his goodwill. Nevertheless, although they had given him no authority, he meant to persist in his endeavour although he risked expulsion from this Paradise of comfort and well-being. He was genuinely anxious to help his uncle, aunt, and cousin, and he thrilled at the thought of crossing swords with Miss Kenyon. If he defeated her, it would, indeed, be a glorious victory. And, possibly, Eleanor would be on his side? He had an amazingly clear picture of her in his mind, a forlorn, independent child, in the midst of the splendours of the Hartling hall. He could see her standing by the side of the colossal elephant's pad; an amazing contrast between the slender and the gross. What was it his uncle had called her? A lovely, solemn little chit? Yes, she was lovely. He had hardly realised it until now. Perhaps she would change her opinion of him after to-morrow. VIII
  • 59. VIII Arthur's usual hour for his morning interview with old Mr Kenyon was 11 o'clock, but two or three times a week he received a message either at breakfast or immediately after, releasing him from attendance. He had been prepared for such a reprieve this morning, imagining that the old man might be a trifle exhausted by his passage of arms with Kenyon Turner the day before, but as no message arrived he went into the library to read the morning papers for an hour and a half before going upstairs. All the important journals were taken at Hartling, most of them in duplicate; and Arthur was probably the only member of the household who had ever considered the expense involved. He had calculated once that, including magazines and other periodicals, more than a hundred pounds a year were spent under this head alone. But the expenditure of the place was all on the same magnificent scale. Arthur remembered his uncle's whimsical comment that cigars were not provided in the workhouse, and smiled grimly at the thought that the inmates of Hartling were the most pampered paupers in the world. The library was empty that morning. Arthur generally found Hubert there at that time, but he had presumably had breakfast even earlier than usual and gone out. Nor did Mr Turner, who came in half an hour later, settle himself down there to his customary study of the Times. Instead he nodded a curt good-morning to Arthur, selected half a dozen papers, and immediately retired with them to some other room. After that Arthur was left severely alone. The inference was clear enough: the Kenyons did not wish to appear in the cause he was going to plead. They might approve his intention but they preferred
  • 60. not to influence it. If he failed, they would deny any kind of responsibility for what he had said. Their attitude had been foreshadowed in the course of their conversation the previous night. No good our interfering, Turner had said. They were afraid of being dismissed from their luxurious almshouse. Arthur put down his paper, walked across to the window, and stood there looking out into the gardens. It had rained heavily in the night and there was more rain coming. Low wisps of ashen gray cloud were travelling intently across the dark purples of the heavy background, and the horizon was hidden by the mist of an approaching downpour. It was not a day, he reflected, remembering many such days, to spend in going from house to house through fountains of London mud; nor in receiving poor patients at the surgery. How their wet clothes reeked! They brought all the worst of the weather in with them, the mud and the wet invaded the consulting room; one was never dry or clean on such days as these. Instinctively he rubbed his hands together, and then looked down at them. They were better kept than when he had first come to Hartling; it had been impossible to keep his hands like that in Peckham. He liked the brown of their tan, deeper on the back than at the finger tips, and his nails were rather good. It was worth while now to spend a little time on them. Were the Kenyons to be pitied? They were not free, of course, but no one was free. They were certainly more free living their life here than he would be if he went back to Peckham. It was a dog's life that, even Somers couldn't deny it. The tall trees in the garden were bent by a rush of wind, and the rain suddenly spattered furiously against the plate glass of the window. How protected one was here! Hartling windows did not rattle in the gale, nor let in the wet. A day such as this gave a zest to the comfort of it all. And although one could not go out there was plenty to do, any amount of books to read, billiards with Turner, and
  • 61. probably they would play bridge in the afternoon—his uncle, Turner, and Elizabeth all played quite a good game.... If the old man turned him out for interfering in a matter in which he was not concerned, he would have to go back to Somers for a night or two. If he was not very careful with the little money still left to him, he would have to give up the idea of Canada altogether. Living in a place like this for five weeks changed one's scale of values. He did not look forward to roughing it so much as he had before he came away from Peckham. Was he pledged in any way to plead Hubert's cause with his grandfather? Would it not be better from every point of view to leave it alone? If Hubert's own family would not put in a word for him, why should a comparative stranger interfere? The old man would almost certainly be annoyed. How on earth could one open the subject to him without impertinence? That offer last night had been made in a moment of sentimental benevolence. He had been worked up by that pathetic story Uncle Joe had told him, and they—he bunched the whole Kenyon family together in this thought of them— could not blame him if he backed out at the last minute. They could not put on airs in that connection. His only regret would be that Miss Kenyon would score. He would have liked to have beaten her, but what possible chance had he of doing that? The fact was that he was standing it all to nothing. He would be a damned fool to risk being turned out of Hartling just now, for the sake of a romantic notion of generosity. It was not as if his pleading were likely to help Hubert; it would probably make things ten times worse for him by putting the old man's back up.... He heard the mellow chime of the hall clock striking eleven, and reluctantly turned to the door. He passed through the main drawing- room on his way, and found all the family except Hubert and Eleanor, sitting there engrossed in their usual occupations. None of them spoke to him as he passed through. Miss Kenyon looked up at him for a moment as he came in, but he could not decide whether her
  • 62. expression was one of challenge or confidence in her own ability to get what she wanted. As he slowly mounted the wide staircase he still saw them all in imagination, waiting with a rather pleasurable excitement for the news of his interview. No doubt they knew well enough that he was going to sign the order for his own exile. Had they waited in just the same way when James Kenyon had defied his father twenty-five years earlier? He paused half-way up the stairs and looked down into the hall. He could see the great elephant's pad standing there, with an effect of gross and imperturbable solidity. Since last night, he had come in some odd way to associate that clumping thing with Eleanor. He could almost see her now, a slender, solemn child, dusty with recent travel, waiting to learn her destiny.... And it was Eleanor whom he saw first when he entered Mr Kenyon's suite of apartments. She had answered his knock—no one went into those rooms without knocking—and he found her standing near the door with an effect of impatience. Are you going to say anything to him about Hubert? she asked at once in a low voice. Arthur hesitated before he said, I've been thinking that perhaps, on the whole, it would be better if I didn't. It might make it worse for him. I've no sort of influence with Mr Kenyon, I mean. She looked at him suspiciously. He could not mistake the doubt in her eyes. She did not believe in the excuse that he had put forward. She had always mistrusted him for some reason or other. Well, have I? he persisted feebly. None whatever, I should imagine, she said; only, I thought.... She paused and looked towards the closed door of the inner room. You're ten minutes late now, she added inconsequently.
  • 63. He was irated by her attitude towards him, her dismissal of him as a person of no importance. He longed to show her that he was not a man to be lightly despised. But all he could find to say was a foolish, petulant accusation of her own motives. Had she not impugned his? No doubt you would be glad enough to see me turned out, he said, with an almost childlike sullenness. You've always disliked me. She stood quite still, staring past him towards the door of her grandfather's room. She was again wearing the dress of pale gray linen in which he had first seen her; and she looked exquisitely sweet, fresh, and young. But he was glad that he had been rude to her. By that rudeness he had shown that he thought of her, and that he resented her opinion of him. He would sooner that she hated him than that she should be indifferent. You think, then, she said, after what seemed to be a long pause, that you might get—turned out, if you said anything to my grandfather about Hubert? You know enough for that? I suppose I know pretty nearly everything there is to know now, he replied sulkily. She looked at him quickly, and then turned her eyes away again. Uncle Joe told you? she asked. With some vague idea of loyalty in his mind, Arthur tried to exculpate his uncle by saying, Partly, yes; but he had nothing to do with the suggestion of my speaking to Mr Kenyon about Hubert. No, of course not, Eleanor said; and in any case you've decided not to. He thought there was still a hint of question in her tone, as if she still hoped that he might be persuaded to champion his cousin's cause; and he grasped the opportunity to get back to the point she had, as he believed, deliberately passed by.
  • 64. You admit that I shan't do any good to Hubert, he said. Why are you so anxious that I should get myself into trouble by interfering— unless it is that you want to be rid of me? Because if that's all, I can go at any time of my own free will. I don't want you to go, she said coldly. Then why are you so keen on—on my taking the chance of offending Mr Kenyon? he insisted. She faced him with a cool, steady stare. You can't seriously believe, she said, that I should be so mean and small as to persuade you into this for any purely selfish purpose of my own? Why, none of them would be as paltry as that. He blushed, but he would not drop his eyes from hers. I'm to respect your motives, of course, he said defiantly; but you're at liberty to impute any sort of cowardice to me? Isn't it cowardice then? she asked, returning his stare without flinching. Haven't you changed your mind because you're afraid of having to leave here? She had defeated him; and realising that he dared not answer that question truthfully, he sought refuge in a youthful petulance. Oh! very well, he said, turning his back on her, and crossing the room towards the inner door. Have it your own way. You can think anything you like about me. I don't care. He knocked and then entered Mr Kenyon's room, without looking back to see what effect this speech might have on her. He was persuaded that he did not care any longer what she thought of him. She was so confoundedly self-sufficient and superior. Mr Kenyon was reading the Times, a thing he could do without the aid of glasses. His sight and hearing were apparently as good as Arthur's own. But he dropped the paper on his knees as Arthur came in.
  • 65. You've been having a talk with Eleanor? he remarked in his usual friendly tone. What a wonderful girl she is, isn't she? I'm surprised that you and she don't get on better together. I had hoped you might be friends. Arthur was slightly taken aback. It had never occurred to him that the old man might wish him to be on more friendly terms with Eleanor. He had never before referred to the subject in any way. Had he, perhaps, heard or guessed at the quarrel between them in the next room? I'm afraid she doesn't like me, he explained. Oh! in that case there's nothing more to be said, the old man replied quietly. Well, you needn't stay this morning, if you've anything else to do. I had meant to send you a message. Arthur understood that he was dismissed, that he might now go back and explain to the people downstairs that he had been given no opportunity to act as the family's catspaw that morning. For twenty-four hours at least he was relieved from any kind of obligation, and in the meantime he could re-discuss the whole question with Hubert and his father. There was but one objection to this plan; he would have to tell Eleanor as he returned through the next room. He sighed and stood irresolute. Mr Kenyon had returned to his study of the Times. No encouragement could be hoped from that quarter. The old man had an amazing gift of detaching his interest from his surroundings. He had probably forgotten that his attendant was still in the room. Why could not Eleanor have undertaken this mission herself? Oh! obviously because she knew that it was futile, purposeless, utterly foolish. Nevertheless, he was not going to be accused of cowardice, nor of trying to propitiate the old man for the sake of being remembered in his will. Might I speak to you a minute, sir? Arthur made his opening curtly, almost contemptuously. By the very act of asking the question he
  • 66. had regained his freedom. He saw that his fear and respect of the old man before him were based on nothing but the longing for comfort and luxury, for abundance and idleness. Now that he had resolved to leave Hartling rather than endure the accusation of cowardice, all his fears had slipped from him. Mr Kenyon put down his paper and looked up. His pale blue eyes were suddenly intent, the eyes of a hunting animal or a bird of prey, in sight but not yet sure of its quarry. Sit down, Arthur, he said quietly, pointing to a chair nearly opposite to his own. You may speak for an hour if you wish. I have nothing to do this morning. It was about Hubert, Arthur said, accepting the invitation to sit down. He did not care now, so far as he himself was concerned, what was the upshot of this conversation, but while he was about it he might as well do his best for poor old Hubert. Mr Kenyon nodded, gravely attentive. No doubt, sir, you'll wonder what concern it is of mine, Arthur continued, but the truth is that I like Hubert, and I'm rather sorry for him.... Sorry for him? Mr Kenyon repeated with a faint surprise. We young men of the present generation, sir, Arthur explained, revelling now in his sense of liberty, think a great deal of our freedom. I don't mean to say that Hubert has any particular ambition in that direction. He was brought up in a different atmosphere. But from my point of view, you see, his life seems dreadfully confined and limited, though perhaps it is a trifle presumptuous for me to be sorry for him on that account. And you wish ...? Mr Kenyon suggested, without the least sign of displeasure.
  • 67. Oh, well! that's another matter, Arthur said. The fact is, sir, that Hubert has fallen in love, and for some reason that I can't pretend to understand, neither he nor my uncle seem to care about coming to ask your consent to his marriage. So—so I've come to plead his cause for him. Who is the girl he wants to marry? Mr Kenyon put in. A change had come over him in the course of Arthur's last sentences. He sat less stiffly in his chair; he had the air of a man re-confronted by some familiar trouble with which he had often battled in the past. Her name is Dorothy Martin, Arthur began. She.... Mr Kenyon interrupted him with a gesture of his hand. I know, he said, her father is Lord Massey's agent—a homely fellow and rather stupid. So Hubert wants to marry Miss Martin, does he? His head drooped a little forward and he began to slide his hands slowly backward and forward along his knees. Arthur felt suddenly sorry for him. Neither Hubert nor his father had told him that Miss Martin's father was, to put it bluntly, not in the Kenyons' class. He understood better now why they had hesitated to approach the old man. And how decently he had taken it! Without any sign of anger, even of vexation. I believe he's very much in love with her, Arthur murmured. Mr Kenyon sighed and sat up. As you remarked just now, Arthur, he said, you naturally can't be expected to understand, and I wonder if it would be indiscreet of a very, very old man to enlighten you? His expression as he spoke was pathetic, wistful; he looked at Arthur as if he besought him to approve the offered confidence. You may be absolutely sure, sir, that I shall not repeat anything you care to tell me, Arthur assured him.
  • 68. Nor let it affect your relations with my family? the old man added, and then while Arthur still sought a convincing reply to that rather difficult question, went on: We are necessarily lonely in our old age, my boy, but I sometimes wonder if my case is not in some ways unusual. Or is it that I have suffered for overstepping the reasonable limit of mortality? He rose from his chair as he spoke and began to pace slowly up and down the room. I have taken a peculiar fancy to you, Arthur, he continued after a brief pause, and I need not be ashamed to tell you why; it is because I admire the independence of your spirit. I liked the way you spoke to me just now; your disregard of what might have been against your own interests; your championship of Hubert. I could wish—I have often wished—that there was more of the same spirit in my own family. Arthur flushed with pleasure. But it seemed to him that he understood now, finally, conclusively, the secret of the Kenyons. They were all cowards, and the old man despised them for their cowardice; not one of them had ever had the courage to stand up to him. If he had, in a sense, bullied them, it was because he had tried to stimulate them into some show of active response. Nevertheless, Arthur attempted an excuse for them. Perhaps, sir, he said, if they had had to face the world as I have.... Mr Kenyon had paused in his walk and now stood in front of him, gravely attentive. But as Arthur hesitated, trying to frame a statement that should not sound too boastful, the old man held up his hand. Well, well, he said, I don't wish to discuss my family with you. My purpose is more selfish than that. I only want you not to misjudge me, as you might very reasonably do, in the circumstances. Downstairs, no doubt, I may sometimes appear in the light of an
  • 69. autocrat. And he lifted his head with a little jerk that wonderfully expressed his own awareness of the absurdity of that accusation. You see, my boy, he went on, resuming his deliberate pacing of the room, I have long been aware that none of my children, unless it be Esther, resemble me in character. They are not, he smiled with an air of excusing his choice of a metaphor, not fighters. There was my poor boy James, Eleanor's father. I don't know if they have told you anything about him? I have heard something, Arthur admitted. Oh, well! then you will understand what a grief his career was to me, Mr Kenyon said with a sigh. I knew his weakness better than he knew it himself, he continued reflectively, but he would not listen to me. I've been forced to take care of them all, because they are none of them able to take care of themselves. I would have saved James, too, if he would have let me. And all I insist upon, in return, is that they should stay here with me, where I can, in a sense, watch over them. Perhaps I'm getting senile. The old habit of thought is too strong in me. If I let them go out into the world, at their age, they would surely be safe enough; but the thought of it arouses all my old uneasiness. But in any case it can't be long now. He had fallen into a brooding monotone, as if he spoke his thoughts aloud; and now he raised himself with an effort and stared at Arthur as though he had become suddenly aware of his presence in the room. So Hubert wants to marry Miss Martin, does he? he asked, returning to the point at issue; and has sent you to plead for him. No, he didn't send me, sir, Arthur explained. It was entirely my own idea. Mr Kenyon smiled paternally. Rash youth! rash youth! he said. Have you no battles of your own to fight?
  • 70. Well, at the moment, no sir, Arthur replied, I have been having a very easy time here for the last five weeks. And now you're pining to get back into the struggle again, eh? Mr Kenyon said, with a lift of his eyebrows. Well, youth and senility are the ages of selfishness, and when there comes a clash between them it is senility that always must give way. And yet, Arthur, I should be so glad if you could stay with me—till the end. I gave you my reasons when we first talked the matter over together. I can add still another, now; I've taken a great liking for you. Are you absolutely determined to go? I? No, sir. I didn't mean ... Arthur stammered. The old man was watching him keenly. But you don't deny that you had that in your mind, when you began to speak to me about Hubert? he said, and then, reading confirmation of that statement in Arthur's embarrassment, he came up to him and laid his hands on his shoulders. Natural enough; natural enough, my boy, he said, there's nothing to be ashamed of. And I wouldn't ask you to make the sacrifice if I were a younger man. But as it is what difference will a year, two years at most, make to you at your time of life? Come, now, he smiled with a flash of roguery, let's make a bargain! Your friend Hubert shall have his Miss Martin, if you'll promise to stay with me and perform those little duties I mentioned when I'm gone. Oh, of course, sir, rather, Arthur said, blushing with pleasure and embarrassment. I would promise that in any case. There's no need for any—any quid pro quo, I mean. Mr Kenyon still had his hands on the young man's shoulders, and he gave him a gentle shake as he said, Very well, that's a bargain then; and I may tell you that you've taken a great weight off my mind. Now, go and tell Hubert to come up to me. I'll promise to let him off more lightly than he deserves.
  • 71. Arthur strode out of the room with the conscious pride of one who has all life at his feet. Eleanor rose from the desk at which she was writing as he entered. So you did speak to him after all? she said, searching his face with an eager, inquiring stare. Yes, I did. It's all right, Arthur returned, disciplining his expression of triumph to a becoming modesty. He wants to see Hubert now. He has promised to let him off lightly, he said. And you're staying on? Eleanor inquired. Yes. He—he made me promise that. Arthur found himself inexplicably dropping into apology. I couldn't possibly refuse him, could I? You see he wants me to be here—at the end. I understand, Eleanor said coldly, turning her back on him and reseating herself at the desk. Will you give Hubert the message or shall I send some one? I'll go, Arthur replied curtly. He was suddenly vexed and disheartened. She had dispersed all the glamour of his achievement; had made him feel as if he had done a mean rather than a splendid thing. There could be but one explanation of her attitude—she suspected him of working on her grandfather's affections. No doubt she knew that he had become a special favourite; had known it probably before he knew it himself. Yet even so, if there were no jealousy on her part—and Uncle Joe had made it certain last night that her motives were above suspicion —why should she be so annoyed? Was she afraid that he might be designing to cut out the rest of the family? He had reached the hall when that explanation came to him, and he paused there, burnt with shame by the bare thought of such a suspicion. It was degrading, infamous. He felt that he could not endure that she should hold such an opinion of him for another
  • 72. moment. He turned back towards the staircase with the intention of instantly challenging her, and then a better means of vindication occurred to him, and he went on into the drawing-room. They were all there now, except Eleanor; and they made no attempt to disguise their interest and excitement. They faced the door with what seemed to be a concerted movement as he entered—and at once misread the signs of his still evident emotion. Miss Kenyon, indeed, made so sure of the correctness of her inference that she acted upon it without further consideration. Arthur saw her then, he believed, in her true character. She rose and came towards him across the room with an effect of vindictive triumph. Her pale blue eyes were bright, the pupils contracted almost to a pin-point; they were the eyes of some fierce bird that is at last within sight of the kill. Well? she said in a clear, cold voice, so you've seen my father. Arthur made no attempt to prevaricate. Yes, he wants to see Hubert, he said, and looked across the room at his cousin as he added, I understand that he won't raise any objection. He saw, as he spoke, the lift of Hubert's head and the quick change of his expression, before his attention was snatched back to Miss Kenyon. And you? she asked sharply. There was no need to put the question more plainly. He knew they all knew what she meant. Your father has asked me to stay on—indefinitely, he said quietly. She made no reply, but she instantly veiled her eyes, lowering her glance to the simple brooch she was wearing at her breast, at the same time putting up a hand as if to adjust it. And when she looked up again her expression betrayed no sign of anger or resentment.
  • 73. He was disappointed. He had expected, even hoped, for some indication of defeat from her. Vaguely he had pictured her going up to her father to enter a violent protest. This apparently meek submission annoyed him. I'm sorry to disappoint you, he said provocatively. I have forgotten the meaning of the word disappointment, she returned gravely, looked him full in the eyes for a moment, and then passed on towards the door. Her self-control was superb, but the picture that remained in Arthur's mind was of her advance towards him across the room. For one instant he had been afraid of her. I say! is it all right, do you think? Hubert eagerly asked, as Arthur joined the group at the farther end of the room. Perfectly all right, old chap—I believe, Arthur replied. Hadn't you better toddle up and see him at once? But what did you tell him? Hubert persisted. Everything I knew, Arthur said. Cut along. I suppose you're very proud of yourself? Elizabeth put in demurely as her brother went out. I'm very glad for Hubert's sake, was Arthur's amendment. Only for his sake? Elizabeth commented carelessly. Turner, with the Times on his knees, was thoughtfully twisting his neat little moustache. So you're going to stay on indefinitely? he remarked. Well, yes; that's to say Mr Kenyon said he would like me to, Arthur replied rather lamely. He was aware of a sense of antagonism between him and the others. None of them so far had shown the least inclination to thank him for acting as their catspaw. All they
  • 74. thought about apparently was the fact that he was going to remain permanently at Hartling. And he knew that the time had come to vindicate his motives, to express that purpose which had come to him in the hall when he relinquished the idea of confronting and, if possible, confounding Eleanor. He drew up a chair and sat down, with an air that he felt claimed his right to be included in the family conclave. I wonder if you'll let me say something to you all about a rather delicate matter? he asked, looking at his uncle. Joe Kenyon raised himself uneasily in his chair and glanced round the faces of the little circle. They were all alert now. There could be no question that they correctly anticipated the nature of the matter the new-comer was going to discuss, although they were uncertain what precisely he might have to say about it. Yes, Arthur, yes. Say anything you like, Joe Kenyon replied rather doubtfully. Now we know that you've come to stay for good, of course, there's no reason why you shouldn't have—well—our confidence. I don't want that, Arthur said. I want to give you mine. I feel, you know, in a confoundedly awkward position, and I'd like to clear it up if I could. I do want you all to understand more particularly that I'm —that I'm not 'on the make' in this business. He paused a moment, but no one made any comment—unless Turner's slight nod of the head could be regarded as an invitation for him to continue. I feel, you see, for one thing, Arthur went on, that I am in a sense at least an outsider, not one of the family anyhow, and I do realise too that the circumstances are pretty well unique. So what I thought of proposing was that I should make some sort of undertaking—I'd put it in writing—that if by any extraordinary chance I should be—be specially favoured later, if you know what I
  • 75. mean, I would hand most of anything I got, back to the family. I should think we could get some sort of binding deed drawn up to that effect, couldn't we? Not until he stopped speaking did Arthur see how terribly he had embarrassed them by thus naming the secret thing in public. Mrs Turner was fumbling with her work; her husband leaned back in his chair, staring up at the ceiling; and Elizabeth, flushing slightly, got up and walked over to the window. It was his aunt who answered him, however, indirectly. Perhaps we'd better go into another room, Catherine, she said, addressing her sister-in-law. I've never been able to understand legal affairs, and this proposal of Arthur's, so far as I understand it, seems to be something of the kind. Mrs Turner grabbed her work and got up with a nod of agreement, but then some purpose seemed to stiffen her. She hesitated, nearly dropped the bead bag she was making, and said in a scarcely audible voice, But we do appreciate the spirit of it all the same. Oh, rather! of course, her brother echoed her. Turner returned to that as an opening, when the three men were left alone to discuss the proposition that had been vaguely indicated. Very decent of you, Woodroffe, he said; and you put the thing quite delicately too; but you understand, don't you, that it would never do to have any kind of formal agreement? I don't. I should prefer it to be as formal and binding as possible, Arthur protested. Joe Kenyon shook his head. No, no, it would never do, he said. You see, my boy, the old man might think we'd been influencing you. Good Lord! I'd make that clear enough to him, Arthur exclaimed. The two older men exchanged a smile that pitied his innocence.
  • 76. You don't know him, Turner remarked caustically. Arthur was a trifle disgusted. He was still warm with gratitude to the old man who had treated him so delightfully that morning, and he resented the bitter note of aspersion in Turner's voice. He has been most frightfully decent to me, he said coldly. Joe Kenyon began to drum on the arm of his chair. Well, no need to go into that, eh, Charles? he asked nervously. The point is—what we've got to make clear to Arthur comes to this, that we're quite glad, what! to trust his word without any damned deeds and so on? Oh, quite! quite! Turner agreed. But you know ... Arthur began to protest. My dear chap, Turner interrupted him, if we can trust you to do the straight thing that's surely all that's necessary. Shake hands on it, if you like; but no parchments, for the Lord's sake. Very good of you, Arthur mumbled, a little overwhelmed by this evidence of their faith in him. If we hadn't trusted you, I couldn't have said what I did last night, his uncle put in. And I for one am very grateful to you for interfering in Hubert's affair. He sighed profoundly as he concluded: It will help him in some ways, I don't doubt. There was apparently nothing more to be said, and Arthur was on his feet preparing to go when Turner remarked casually to his brother-in-law, Totting 'em up pretty fast just now, isn't he? That'll make three more of us if poor old Ken has to come in. Joe Kenyon's only reply was to draw down the corners of his mouth and raise his eyebrows. Arthur did not want to hear any more. He was sorry that he had heard so much. These petty criticisms of old Kenyon made him despise Turner and his uncle; they represented another aspect of
  • 77. their cowardice. Damn it, the old man was worth the lot of them, if you excluded Eleanor. He supposed that she would hear of his agreement with the family, and wondered if she would apologise to him. IX
  • 78. IX Arthur received a letter from Somers by the second post. It was still raining, and he was playing billiards with Turner when the letter arrived, so he did not open it until after tea. Somers had written in a mood of depression. Bates, Arthur's successor at the Peckham surgery, was not a success. The fool means well, too well, Somers wrote; but I was wrong in anticipating that the panel patients would like him. They don't. They have taken his measure, and all his good intentions can't disguise the fact that he is pudden-headed. When are you going to Canada? If you are going? Isn't that visit of yours being amazingly protracted? I suppose you're lapped in luxury and can't tear yourself away. Or have you got a permanent job there as tame medico to the old man? Or is it a girl? I wish to God you would write and tell me in any case. I can't keep Bates (he has got on my nerves) and I should like to know for certain if there is the least hope of your coming back. I can't see you marrying for money, and if the hypothecated girl is the right sort, she would face the world with you on five hundred a year. I might make it up to that. The private practice is better than it was. Sackville, who has been here so long, is getting too old. You and I between us would get pretty nearly all the new people. And if my first guess was the right one and you've got some sort of sinecure in the Hartling household, the sooner you chuck it the better, my son. For one thing you'll get soft, and for another you'll get no experience. If you were doing hospital work (which you ought to be), I should not try to tempt you away, but if you are just letting your mind rot, I shall think it is my duty to save you at any cost. As he read, Arthur lost the sense of his surroundings. He visualised the narrow sitting-room of the little Peckham house, and heard Somers's voice telling him that he ought to be doing hospital work or
  • 79. getting varied experience in a general practice; that he was becoming soft, going to pieces from a professional point of view. He blushed like a student under the rebuke of the demonstrator. Then he looked up and the illusion vanished. He saw that all his circumstances were now changed. All that advice would be sound enough if he were forced to return to such a general practice as Peckham. But if the old man left him, say £10,000, he might have a shot for his Fellowship; try for a registrarship at one of the bigger hospitals; perhaps get on the staff of one and set up in Wimpole Street. With a certain amount of capital, this would be so much easier, and the war had given him a taste for minor surgery. Indeed, it had always appealed to him more than medicine. Meanwhile, it was true that he must not let himself get rusty. He ought to go on reading, order some books from town; or at least have the Lancet sent to him every Friday. He must keep himself up to date while he was waiting. At the outside, he could not have to wait more than five years. He would only be thirty-three then.... He paused doubtfully on that thought, but just then Hubert came in, and the moment of uneasiness passed and was forgotten. It had stopped raining and Hubert thought that they might put in nine holes before dinner. It was made clear on the way up to the links, however, that golf was not Hubert's goal on this occasion. He had a wild hope that Miss Martin might be found at the Club House. He had wanted, naturally enough, to tell her at once that the engagement was to be permitted, but his grandfather had sent him up to the farm on a job that had kept him busy all the afternoon. Probably did it just to tantalise me a bit, Hubert complained; teach me that I couldn't have everything my own way. Oh, surely not! Arthur protested. He was offended, again, by this imputation of unworthy motives to old Mr Kenyon. I don't believe any of you understand him, he continued warmly. We had quite a
  • 80. long talk this morning and he rather came out of his shell. He may seem a bit hard and inhuman at times, you know, but underneath, I'm certain he's trying to do the best for everybody. Hubert looked faintly surprised. Oh! that was the way he took you, was it? he remarked. There you go again, Arthur said. You, all of you, seem to have made up your minds that—that—I don't know—— He could not complete his sentence. He could see that they all feared the old man, but they never brought any explicit charge against him unless it were that he bullied them into staying on at Hartling. And all that had been explained. Arthur, remembering his conversation of the morning, was strongly inclined now to take the old man's side. He knew their weaknesses. They were a poor lot obviously. They lacked independence of spirit; if they were allowed to go out into the world they would come awful croppers like the unfortunate, hot-headed James, Eleanor's father. The old man had learnt a lesson in the course of that affair. He was a bit of an autocrat, no doubt; but he had good reason to be, with a family that could not be trusted. Hubert appeared either unwilling or unable to provide a definition of the family's attitude. Oh, well, he said, no good discussing that, is it? Here we are and we've got to put up with it. And, personally, you know, I don't care much now—partly thanks to you, old man. Only partly, Arthur reflected, but he made no comment on that. That's all right, then, was all he said. Hubert was in luck, for Miss Martin was at the Club House, drawn thither, no doubt, by the same hope that had stimulated her lover, and although they cheerfully proposed a foursome, Arthur knew that they would sooner be alone, and declined. The proposed fourth player in the case was Fergusson, the general practitioner from the village, to whom reference had been made when the post of medical attendant had been first offered to Arthur. He and Fergusson had
  • 81. met once or twice on the links, but their brief conversations had so far been limited to golf. The doctor was a man of sixty or so, with thick gray hair and moustache and a strong, clumsy figure. Arthur had formed the opinion that he was rather a surly fellow. Care to take me on for nine holes—haven't time for more? Arthur asked him. Fergusson nodded. Not that I'm particularly anxious to play, he said. The ground will be very wet, I'm thinking, after all the rain we've had to-day. I just looked in on my way home, without much idea of getting a game. Indeed, to be honest, I've had a very long day and am not so anxious to exert myself. Scattered sort of practice, I expect, Arthur commented. Have a cigar. Fergusson accepted the cigar with a nod of thanks. One of your perquisites? he asked, smiling rather grimly. Arthur stiffened. Never thought of it like that, he said. They're all over the shop up there. You just take 'em as you want 'em. No need to get ruffled, Fergusson replied quietly. I know. I used to be up there once a week or so before you came. Nice little sinecure. But I say, look here, Arthur said, suddenly conscious for the first time that he might have been guilty of a breach of medical etiquette, you don't mean to tell me that I've taken away one of your cases? Fergusson laughed dryly. Well, you have and you haven't, he said. But your conscience is no doubt clear enough and everything was done in proper form. The old man wrote to me and explained, and I went up and talked it all over with him. You were playing golf on that occasion, I'm thinking. However, it'll be a soft job for you. Arthur still looked uneasy. I never once thought about you in that connection, you know, he said. I ought, anyhow, to have come and
  • 82. Welcome to Our Bookstore - The Ultimate Destination for Book Lovers Are you passionate about books and eager to explore new worlds of knowledge? At our website, we offer a vast collection of books that cater to every interest and age group. From classic literature to specialized publications, self-help books, and children’s stories, we have it all! Each book is a gateway to new adventures, helping you expand your knowledge and nourish your soul Experience Convenient and Enjoyable Book Shopping Our website is more than just an online bookstore—it’s a bridge connecting readers to the timeless values of culture and wisdom. With a sleek and user-friendly interface and a smart search system, you can find your favorite books quickly and easily. Enjoy special promotions, fast home delivery, and a seamless shopping experience that saves you time and enhances your love for reading. Let us accompany you on the journey of exploring knowledge and personal growth! ebookgate.com