# qouoww

 博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

## 高级概念

The Code First modeling functionality that you have seen so far should be enough to get you up and running with most applications. However, Code First also includes some more advanced functionality that you may require as your needs advance. Throughout this book you’ve seen Code First’s conventions in action, but if there are one or more conventions you don’t like, Code First allows you to remove them. You may also want to get rid of that EdmMetadata table Code First is adding to your database. Code First caches its model by default, and it’s possible to override that behavior to solve problems like targeting multiple database providers in the same application in stance. This chapter will cover these topics and more.

##### 映射到非表数据库对象

So far you have used Code First to map to tables, whether you are generating a database or mapping to tables in an existing database. But databases support many other types of objects, including stored procedures and views.
As of Entity Framework 4.2, Code First only has built-in support for tables, meaning that it is only capable of generating schemas that contain tables. Therefore, if you are using Code First to generate your database, you are restricted to tables.
However, if you are mapping to an existing database, you may have views, stored procedures, and other objects in the database you are mapping to. Let’s take a look at how we can interact with those.

You have the option of manually editing the database schema after Code First has created it. If you do manually edit the database to include nontable objects, you can apply the same techniques discussed in this section.
The Entity Framework team has indicated that they plan to add support for mapping to other database objects in future releases.

EF框架开发团队声称他们已经计划在未来的版本技持非表数据库对象的映射。

##### 映射到可更新的视图

In some cases you may want to simply map an entity to a view rather than a table. For example, you may be mapping to a database that has a very large and confusing schema.To simplify things, the database might contain a view that exposes the data for your entity with more comprehensible column names. If the view is updatable, you can use the Entity Framework to insert, update, and delete data as well as selecting it. Fortunately, most databases, including SQL Server, use the same SQL syntax for interacting with views as they do for tables. This means you can simply “lie” to Code First and tell it that the view is a table. You do this by using the same configuration you use for naming tables.

For example, perhaps you want the Destination data to come from an updateable view called my_destination_view rather than a table. You can use the Table annotation to specify the view name:

[Table("my_detination_view")]
public class Destination

Alternatively, you can use the ToTable method from the Fluent API to map to the view:

modelBuilder.Entity<Destination>().ToTable("my_detination_view");
##### 使用视图填充对象

Not all scenarios call for mapping an entity directly to an updateable view. You may find yourself wanting to leave a class mapped to a table but to have the ability to use a view to retrieve a set of those classes in a particular scenario. For example, let’s assume that you want to leave Destination mapped to the Destinations table, but in one area of your application you want to load all the destinations from the TopTenDestinations view. You can use the SqlQuery method on DbSet to load entities based on some SQL that you write:

var destinations = context.Destinations
.SqlQuery("SELECT * FROM dbo.TopTenDestinations");


In the above code we are using a SQL statement that bypasses Entity Framework to get back the desired Destination objects. The good thing is that once those objects are retrieved from the database, they are treated exactly the same as objects that were loaded any other way. This means you still get change tracking, lazy loading, and other DbContext features for the Destination objects that were loaded.

The SqlQuery method relies on an exact match between the column names in the result set of the query you wrote and the names of the properties in your object. Because the Destination class contains DestinationId, Name, and other properties, the view must return columns with these same names. If the view does not have the same column names as the properties on your class, you will need to alias the columns in your select statement.
For example, let’s say that your TopTenDestinations view uses Id instead of DestinationId for the primary key name. In SQL Server, you can use the AS word to  alias the Id column from the view as the DestinationId column that Entity Framework
is expecting, as you can see in Example 7-1.

SqlQuery函数的方法依赖于在查询结果集的列名和对象属性名的精确匹配。由于目标类包含DestinationId，Name和其他属性，视图必须返回与其相同名称的列。如果视图没有与类属性相同的列名，需要在SELECT语句中的为列设置别名。

Example 7-1. Querying a database view from a DbSet

var destinations = context.Destinations
.SqlQuery(@"SELECT
Id AS DestinationId,
Name,
Country,
Description,
Photo
FROM dbo.TopTenDestinations");

Note that the column-to-property name matching does not take any mapping into
account. For example, if you had mapped the DestinationId property to a column
called Id in the Destinations table, the SqlQuery method would not use this mapping.  The SqlQuery method always attempts the column-to-property matching based on property name. Therefore, the column in the result set would still need to be called DestinationId.

##### 使用视图来填充非模型对象

The two techniques we have looked at so far allow you to use a view to populate a set of objects that are part of your model. Once these objects are created, they are tracked by the context and any changes will be written back to the database. You may find yourself wanting to get the results of a view back into a read-only set of objects. The results of the view may combine data from multiple tables and therefore can’t be mapped directly to an entity that is part of your model.
For example, you may have a view called DestinationSummaryView that combines data from the Destinations and Lodgings tables. This view may have DestinationId, Name, LodgingCount, and ResortCount columns. These columns don’t match any of the entities in the BAGA model, but it would be great to be able to get the results back into a purpose-built object that you can then use in your application.

The DestinationSummary class might look something like Example 7-2.

DestinationSummary类类似代码7-2所示：

Example 7-2. DestinationSummary implementation

public class DestinationSummary
{
public int DestinationId { get; set; }
public string Name { get; set; }
public int LodgingCount { get; set; }
public int ResortCount { get; set; }
}

Because the class isn’t part of the BAGA model, you can’t use a DbSet to query for
results. Instead, you use the SqlQuery method on DbContext.Database as follows:

var summary = context.Database.SqlQuery<DestinationSummary>(
"SELECT * FROM dbo.DestinationSummaryView");

In response, Entity Framework will run the SQL that you supplied to access the DestinationSummaryView view. It will then take these results and try to match the column names up with the property names of the DestinationSummary class that you specified in the generic argument of SqlQuery. Because the column and property names match, we will get the results of the query in a collection of DestinationSummary objects.
Because we didn’t go through a DbSet as we did in Example 7-1, the DestinationSummary objects that are created are not tracked by the context. Therefore, if you change any of the properties, Entity Framework will not pay any attention to those changes any time SaveChanges is called.

##### 使用存储过程

Code First does not have any support for mapping Insert, Update, and Delete statements for your classes directly to stored procedures, as you are able to do in the designer.

Code First并不支持直接通过存储过程来映射插入，更新和删除命令，但是你可以在设计器中使用这些命令。

Using the same techniques you just saw for working with views, you can also use stored procedures to fetch results from the database. For example, let’s say you have a Get TopTenDestinations stored procedure that takes a single parameter to specify in which country to look for destinations. You can use the SqlQuery method on DbSet to execute this procedure:

var country = "Australia";
var destinations = context.Destinations
.SqlQuery("dbo.GetTopTenDestinations @p0", country);

Notice that SqlQuery accepts parameters. See the sidebar “SqlQuery Parameters to

As you saw above with views, you can also use the DbContext.Database.SqlQuery method to get back results from stored procedures that don’t match an entity in your model. Let’s assume you have a GetDestinationSummary stored procedure and you want to get the results in a collection of the DestinationSummary class you saw back in Example 7-2. Let’s also say this stored procedure takes two parameters—one for the country and the other for some keywords:

var country = "Australia";
var keyWords = "Beach, Sun";
var destinations = context.Database.SqlQuery<DestinationSummary>(
"dbo.GetDestinationSummary @p0, @p1", country, keyWords);


In the above code, you can see that we’re using index-based naming for parameters. As noted in the sidebar, Entity Framework will wrap these parameters up as DbParameter objects for you to avoid any SQL injection issues. The column names in the result returned by the stored procedure will be matched with the property names on DestinationSummary. Because DestinationSummary isn’t part of the BAGA model, the results are not tracked and any changes will not be pushed back to the database.

SqlQuery Parameters to Prevent SQL Injection
The SqlQuery method allows you to specify parameters. Entity Framework will take
care of wrapping these into DbParameter objects to help prevent against SQL injection attacks. You use a @p prefix for parameters followed by an integer index. Entity Framework will then match these indexes up with the list of parameters you provide after the query string. As with the view-based example you saw earlier, the results of the query are tracked by the context and behave the same as results of any other query.

SqlQuery方法允许指定参数。EF框架将封装参数为DbParameter对象以防止SQL注入式攻击。使用对参数@p前缀后附一个整数索引号来表达。EF框架将以这些索引号匹配在查询字符串中提供的参数值。正如你在前面看到的基于视图的案例，查询结果可以被上下文所跟踪，像其他查询结果一样使用。

##### 删除默认规则

In previous chapters you have seen that Code First includes a set of conventions that help build your model. You’ve seen how you can supplement or override what the conventions do using Data Annotations or the Fluent API. One other option you have is to switch off one or more of the default conventions.
Each Code First convention is implemented as a class in the System.Data.Entity.ModelConfiguration.Conventions namespace. Code First currently only allows you to remove one or more of the included conventions.

The ability to write your own conventions was included in a preview of Code First. However, the Entity Framework team removed this functionality because they felt that they didn’t have time to polish the design and get to the appropriate quality level without holding up the muchawaited release of Code First. It’s likely this feature will become available again in a future release.

A full list of the Code First conventions that can be removed and a description of what each convention does is available at http://msdn.microsoft.com/en-us/library/
gg696316(v=VS.103).aspx. The complete list of conventions is also shown in
Figure 7-1

While you can remove any of the conventions listed in Figure 7-1, we’ll use just one

While you could just override the cascade behavior for every required relationship, if  you have a lot of relationships, it may make more sense just to disable the convention altogether.
Switching off conventions is done in the OnModelCreating method on your context via the DbModelBuilder.Conventions.Remove method. Add the following line of code to OnModelCreating in your BreakAwayContext class:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

The model contains a required relationship between Lodging and Destination. Up until now, Code First has been automatically adding a cascade delete rule to this relationship.With the new code in place, run the application so that the database gets recreated and this cascade delete will be removed from this relationship in the model and in the database (Figure 7-2). It will also disappear from any other required relationships that may exist.

After switching off the conventions, you may decide that you want to reintroduce cas
cade delete behavior on some of the relationships. You can do this using the Fluent API as described back in Chapter 4.

In the BAGA model, it makes sense for us to have cascade delete enabled on required relationships, so go ahead and re-enable the OneToManyCascadeDeleteConvention convention by removing the modelBuilder.Conventions.Remove call we just added.

##### 控制模型缓存

Throughout this book you have seen how Code First takes care of a lot of things for you, but that you can take control of them and change the behavior when needed. Model caching is no exception; in fact, you likely had no idea that Code First was caching a model for you up to this point. After scanning your classes and applying conventions and configuration, Code First keeps an in-memory version of your model around so that it can be reused in the application instance. This is the reason that the OnModelCreating method is only hit once for each DbContext in an application instance. In this section, you will learn more about what model caching is, when you might need to override the conventions, and how you go about doing that.

##### 理解模型缓存

In earlier chapters, you have seen that Code First will automatically discover and build a model based on the DbSet properties that you expose on your context. The model  creation process involves taking that set of entity types, running the Code First conventions on them, and then applying any additional configuration that you specified via Data Annotations or the Fluent API. This process isn’t cheap on resources and can take some time, especially if your model is large and/or complex. To avoid incurring this cost every time you create an instance of your context, Code First runs this process once and then caches the final model for your context type. Model caching occurs at the AppDomain level.
You can see model caching in action by monitoring when the OnModelCreating method is called on your context. Add a line to the OnModelCreating method that will write to the console whenever it is called:

Console.WriteLine("OnModelCreating called!");
Modify the Main method to call the InsertDestination method a number of times
(Example 7-3). You added the InsertDestination method itself back in Chapter 2.

修改Main方法调用InsertDestination方法一定次数（代码7-3）。InsertDestination方法见第2章。

Example 7-3. Main updated to use the context multiple times

static void Main()
{
Database.SetInitializer(
new DropCreateDatabaseIfModelChanges<BreakAwayContext>());
InsertDestination();
InsertDestination();
InsertDestination();
}

After running the application again, you will see that although the code constructs and uses three separate instances of BreakAwayContext, the OnModelCreating method is only called once. This is because Code First only calls OnModelCreating while creating the model for the first context instance; after that, the final model is cached and is reused for the following uses of BreakAwayContext.

##### 覆写默认的模型缓存

There aren’t many situations where you need to take control of model caching. Provided that the model for a given context type stays the same for every instance of that context with an AppDomain, the default behavior is going to work as expected. Using the default behavior is also going to give you the best performance, because model creation will only occur one time.
There are some situations where the model for a given context type may vary between instances in the same AppDomain. One example would be using a multitenant database. A multitenant database involves having the same model replicated multiple times in the same physical database. For example, you may have a model that is used to store blog posts and a website that displays them. Your website might contain a personal and a work blog that both use the same model. In the database you could have the tables used to store the data for this model replicated in two separate schemas. The tables for your work blog may live in the work schema (work.Posts, work.Comments, etc.) and the tables for your personal blog might live in the personal schema (personal.Posts, personal.Comments, etc.). Each of these sets of tables is known as a tenant. Database schemas are just one way to distinguish between tenants; there are many other patterns, such as table prefixes.
If your application needs to access multiple tenants from the same AppDomain, the mapping between classes and tables is going to be different depending on what tenant you are targeting. Different mapping means a different model, which in turn means the default model caching won’t work for you.

Another example would be using the same context to target the same model on different database providers in the same AppDomain. Different database providers means different data types for the columns in the database, which in turn means a different model. Let’s take a look at this scenario and how to handle model caching.
Add the TargetMultipleProviders method shown in Example 7-4. This method uses
the same context to access a SQL Server and SQL Server Compact Edition database.

You will need the SQL Server Compact Edition runtime installed to complete this section. If you have completed Chapter 6, you have already installed the runtime. If not, see “Installing SQL Server Compact Edition” on page 135. You may also remember that in Chapter 6 we had to change our model to target SQL Compact. If you want to test out this code, you will need to make the same change again here. Back in Chapter 3, we configured Trip.Identifier to be a database-generated key.
Identifier is a GUID property, and SQL Server had no problem generating values for us. SQL Compact, however, isn’t able to generate values for GUID columns. If you want to run the application, remove either the Data Annotation or Fluent API call that configures Trip.Identifier as database-generated.

Example 7-4. Reusing a context to target multiple providers

static void Main(string[] args)
{
Database.SetInitializer(new
DropCreateDatabaseIfModelChanges<BreakAwayContext>());
TargetMultipleProviders();
}
private static void TargetMultipleProviders()
{
var sqlString = @"Server=.\SQLEXPRESS;
Database=DataAccess.BreakAwayContext;
Trusted_Connection=true";
using (var connection = new SqlConnection(sqlString))
{
using (var context = new BreakAwayContext(connection))
{
context.Destinations.Add(new Destination { Name = "Hawaii" });
context.SaveChanges();
}
}
var sqlCeString =
@"Data Source=|AppData|\DataAccess.BreakAwayContext.sdf";
using (var connection = new SqlCeConnection(sqlCeString))
{
using (var context = new BreakAwayContext(connection))
{
context.Destinations.Add(new Destination { Name = "Hawaii" });
context.SaveChanges();
}
}
}

Run the application. You will get an exception when trying to use the context instance that targets SQL Server Compact Edition. This will be a NotSupportedException stating that, “Using the same DbCompiledModel to create contexts against different types of database servers is not supported. Instead, create a separate DbCompiledModel for each type of server being used.”
To use the same context type with different models in the same AppDomain, you need to externally build a DbCompiledModel for each model and then use these to construct the different context instances. DbContext exposes a set of constructors that allow you to supply the model to be used, along with connection information. Add a constructor to the BreakAwayContext class that allows a DbCompiledModel and a DbConnection to be supplied:

public BreakAwayContext(DbConnection connection,
DbCompiledModel model)
: base(connection, model, contextOwnsConnection: false)
{ }

The code in Example 7-5 shows an updated TargetMultipleProviders method that demonstrates how this constructor can now be used to target different database providers, using a different model for each.

Example 7-5. Code updated to work with multiple providers

private static void TargetMultipleProviders()
{
var sql_model = GetBuilder().Build(
new DbProviderInfo("System.Data.SqlClient", "2008"))
.Compile();
var ce_model = GetBuilder().Build(
new DbProviderInfo("System.Data.SqlServerCe.4.0", "4.0"))
.Compile();

var sql_cstr = @"Server=.\SQLEXPRESS;
Database=DataAccess.BreakAwayContext;
Trusted_Connection=true";

using (var connection = new SqlConnection(sql_cstr))
{
using (var context =
new BreakAwayContext(connection, sql_model))
{
context.Destinations.Add(new Destination { Name = "Hawaii" });
context.SaveChanges();
}
}

var ce_cstr =
using (var connection = new SqlCeConnection(ce_cstr))
{
using (var context = new BreakAwayContext(connection, ce_model))
{
context.Database.Initialize(force: true);
context.Destinations.Add(new Destination { Name = "Hawaii" });
context.SaveChanges();
}
}
}
private static DbModelBuilder GetBuilder()
{
var builder = new DbModelBuilder();
builder.Entity<Activity>();
builder.Entity<Destination>();
builder.Entity<Hostel>();
builder.Entity<InternetSpecial>();  builder.Entity<Lodging>();
builder.Entity<Person>();
builder.Entity<PersonPhoto>();
builder.Entity<Reservation>();
builder.Entity<Resort>();
builder.Entity<Trip>();
builder.ComplexType<Measurement>();
builder.ComplexType<PersonalInfo>();
return builder;
}


Let’s walk through what the code in the TargetMultipleProviders method is doing. The GetBuilder method is responsible for creating a DbModelBuilder and registering all your classes with the builder. The code in the example registers each class using the DbModelBuilder.Entity and DbModelBuilder.ComplexType methods. This approach will work if you have been using Data Annotations to configure your classes. If you have been using the Fluent API, you should copy the code from your OnModelCreating method to replace this code. Note that you also need to include the EdmMetadata class and map it to the EdmMetadata table; this allows Code First to detect when the model and database go out of sync. When DbContext is responsible for building the model, it will take care of adding this class for you.

The next step is to build and compile the model for the two providers that are going to be targeted. In the example, the invariant name and manifest token for the database provider are supplied to the Build method. As an alternative, there is another overload of Build that accepts a DbConnection to get the provider information from.

With the compiled models created, they can now be used to access the two different databases. Remember that database initialization only occurs once per AppDomain, so only the first database to be used will be initialized automatically. The call to Database.Initialize on the context targeting the second database ensures that the second database is also initialized.
In the end, the new Destination is added to two different databases using the same set of classes and configurations to define duplicate models. Now that we’re done using SQL Compact, go ahead and re-enable the configuration to make Trip.Identifier database-generated.

Remember that building and compiling the model are expensive operations. The resulting compiled model should be cached and reused for all context instances that target the same model.

#### Working with the EdmMetadata Table

Back in Chapter 2, you learned that, by default, Code First adds an EdmMetadata table to your database. There are some advantages in allowing Code First to have this table in the database, but you also have the option of removing it. In this section, you will see how to remove the EdmMetadata table from your database. You’ll also learn about the implications of removing it.
The EdmMetadata table serves a single purpose, and that is to store a snapshot of the model that was used to create the database. Having the snapshot allows Code First to check whether the current model matches the current database or not. The snapshot is stored by taking a SHA256 hash of the database portion of the model. You can see in Figure 7-3 that the EdmMetadata table always contains a single row with the hash stored in it.

Code First uses the EdmMetadata table in the included database initializers, but you can also interact with it programmatically using the EdmMetadata class in the EntityFramework API. Modify the Main method to call a new UseEdmMetadataTable method, shown in Example 7-6, to experiment with this class:

Example 7-6. The UseEdmMetadata method
static void Main()
{
Database.SetInitializer(
new DropCreateDatabaseIfModelChanges<BreakAwayContext>());
}
{
using (var context = new BreakAwayContext())
{
Console.WriteLine("Current Model Hash: {0}", modelHash);
var databaseHash =
Console.WriteLine("Current Database Hash: {0}", databaseHash);
var compatible =
context.Database.CompatibleWithModel(throwIfNoMetadata: true);
Console.WriteLine("Model Compatible With Database?: {0}",
compatible);
}
}

This code starts by using the static EdmMetadata.TryGetModelHash method to find the hash for the current model. This method will always work for Code First models, but if you attempt to use it with a model created using the designer, it will return null. The EdmMetadata class is included as part of your model, so you can use your DbContext to interact with it.
The second section of code creates a DbSet for the EdmMetadata class and then asks for the single row of data so that it can read the hash value from it. Finally, there is a    DbContext.Database.bvgt65 method that makes it simple to check if the model and database match. This is the method that the database initializers included in the Entity Framework make use of. Specifying true for the throwIfNoMetadata parameter will cause an exception to be thrown if the EdmMetadata table has been excluded from the database. Specifying false will cause the method to return false if the table is excluded. You can run the code and see that everything currently matches.

##### 在Code First中使用ObjectContext

Up until now, you have seen Code First being used with the DbContext API, which is    the recommended API surface for working with Code First. DbContext was  introduced in Entity Framework 4.1 as a lighter-weight and more productive wrapper over the existing Entity Framework components. The alternative to DbContext is the ObjectContext API, and while it is recommended to use DbContext with Code First, it is still possible to use ObjectContext. In this chapter, you will see how to build a Code First model and use it to construct an ObjectContext.

DbContext or ObjectContext?
DbContext is simply a wrapper over ObjectContext and associated classes. If you need some of the more advanced features that are only available from ObjectContext, you can cast DbContext to the IObjectContextAdapter interface to access the underlying ObjectContext. This approach allows you to access the functionality from ObjectCon text while still being able to write most of your code against the newer DbContext. You might consider using Code First with ObjectContext if you have existing applications that are based on ObjectContext and you are swapping from Model First or Database First to Code First.

Similar to using a DbContext-based context, you start by creating a derived context,   except this time it derives from ObjectContext and exposes ObjectSet properties instead of DbSet properties. Notice in Example 7-8 that when using an ObjectContext, you need to write a bit more code than with the DbContext. You must expose a constructor that accepts an EntityConnection. The ObjectSet properties also need to be initialized using the CreateObjectSet method; this is something DbContext takes care of for you.

Example 7-8. Implementing ObjectContext

using System.Data.EntityClient;
using System.Data.Objects;
using Model;
namespace DataAccess
{
public class BreakAwayObjectContext : ObjectContext
{
public BreakAwayObjectContext(EntityConnection connection)
: base(connection)
{
this.Destinations = this.CreateObjectSet<Destination>();
this.Lodgings = this.CreateObjectSet<Lodging>();
this.Trips = this.CreateObjectSet<Trip>();
this.People = this.CreateObjectSet<Person>();
this.PersonPhotos = this.CreateObjectSet<PersonPhoto>();
}
public ObjectSet<Destination> Destinations { get; private set; }
public ObjectSet<Lodging> Lodgings { get; private set; }
public ObjectSet<Trip> Trips { get; private set; }
public ObjectSet<Person> People { get; private set; }
public ObjectSet<PersonPhoto> PersonPhotos { get; private set; }
}
}

At this point, DbContext would take care of scanning the DbSet properties and building a model based on them. But ObjectContext has no built-in support for Code First. Code First provides a method to bridge this gap—DbModelBuilder.UseObjectContext. In the following walkthrough, you’ll learn how to leverage this to create an ObjectContext from a Code First model.

DbContext能够自动扫描DbSet属性并创建一个基于这些属性的模型。但是ObjectContext没有为Code First内建支持。Code First提供了一个方法来填补这个鸿沟，这个方法就是DbModelBuilder.UseObjectContext. 下面，我们来看如何使用这个方法在Code First模型中创建ObjectContext.

Modify the Main method to make use of a new UseObjectContext method, as shown in Example 7-9.

Example 7-9. Code updated to use BreakAwayObjectContext

static void Main()
{
UseObjectContext();
}
private static void UseObjectContext()
{
var builder = GetBuilder();
var cstr = @"Server=.\SQLEXPRESS;
Database=BreakAwayObjectContext;
Trusted_Connection=true";using (var connection = new SqlConnection(cstr))
{
var model = builder.Build(connection).Compile();
using (var context =
model.CreateObjectContext<BreakAwayObjectContext>(connection))
{
if (!context.DatabaseExists())
{
context.CreateDatabase();
}
new Destination { Name = "Ayers Rock" });
context.SaveChanges();
}
} 

You start by creating a DbModelBuilder, using the GetBuilder method we added earlier in this chapter. You then use the model builder to create a model and compile it. Note that you must supply the connection or provider information when building the model, as the provider affects the data types, etc. in the resulting model. With the model compiled, you can then use the CreateObjectContext method to construct the ObjectContext. This method relies on the constructor you exposed that accepts a single Entity Connection parameter. ObjectContext doesn’t support database initializers, so you also need to write code to check if the database exists and create it if it does not. Note that ObjectContext does not support EdmMetadata either, so there is no way to detect if the model and database are compatible.

#### 小结

In this chapter, you saw a variety of advanced features that Code First provides. These features are provided to make sure that you can override what Code First does by default in situations where the default behavior just doesn’t work for your scenario. Most applications won’t require you to use these features, but it’s good to have an understanding of what is available in case you ever need them.
We’ve now covered Code First from top to bottom. We started with a high-level overview of what Code First is. You then learned how Code First builds the model and how you can customize the model by using configuration. You saw how to influence which database Code First connects to and how that database is initialized. And finally in this chapter, we wrapped things up with some advanced topics.
The final chapter of this book will help prepare you for what’s coming in future releases of Code First.

posted on 2012-01-06 20:26  qouoww  阅读(6087)  评论(5编辑  收藏