【配置属性】—Entity Framework实例详解(DataAnnotations and Fluent API)

来源:互联网 发布:第三方软件测试服务 编辑:程序博客网 时间:2024/05/16 17:15

Video demonstrating this article


Entity Framework Code First modeling workflow allows you to use your own domain classes to represent the model which EF relies on to perform querying, change tracking and updating functions. Code first leverages a programming pattern referred to as convention over configuration. What this means is that code first will assume that your classes follow the conventions of the schema that EF uses for a conceptual model. In that case, EF will be able to work out the details it needs to do its job. However, if your classes do not follow those conventions, you have the ability to add configurations to your classes to provide EF with the information it needs.

Code first gives you two ways to add these configurations to your classes. One is using simple attributes called DataAnnotations and the other is using code first’s Fluent API, which provides you with a way to describe configurations imperatively, in code.

This article will focus on using DataAnnotations (in the System.ComponentModel.DataAnnotations namespace) to configure your classes – highlighting the most commonly needed configurations. DataAnnotations are also understood by a number of .NET applications, such as ASP.NET MVC which allows these applications to leverage the same annotations for client-side validations.

I’ll demonstrate code first DataAnnotations with a simple pair of classes: Blog and Post.

  1. public class Blog
  2.   {
  3.     public int Id { get; set; }
  4.     public string Title { get; set; }
  5.     public string BloggerName { get; set;}
  6.     public virtual ICollection<Post> Posts { get; set; }
  7.   }
  8.   public class Post
  9.   {
  10.     public int Id { get; set; }
  11.     public string Title { get; set; }
  12.     public DateTime DateCreated { get; set; }
  13.     public string Content { get; set; }
  14.     public int BlogId { get; set; }
  15.     public ICollection<Comment> Comments { get; set; }
  16.   }

You can read the whitepaper, Building an MVC 3 App with Code First and Entity Framework 4.1, which demonstrates using these classes with Entity Framework 4.1 Code First in an MVC 3 application. I’ll use the same application to demonstrate Data Annotations in action.

As they are, the Blog and Post classes conveniently follow code first convention and required no tweaks to help EF work with them. But you can also use the annotations to provide more information to EF (and to MVC 3) about the classes and the database that they map to.

Key

Entity Framework relies on every entity having a key value (aka EntityKey) that it uses for tracking entities. One of the conventions that code first depends on is how it implies which property is the key in each of the code first classes. That convention is to look for a property named “Id” or one that combines the class name and “Id”, such as “BlogId”. In addition to EF’s use of the EntityKey, the property will map to a primary key column in the database.

The Blog and Post classes both follow this convention. But what if they didn’t? What if Blog used the namePrimaryTrackingKey instead or even foo? If code first does not find a property that matches this convention it will throw an exception because of Entity Framework’s requirement that you must have a key property. You can use the key annotation to specify which property is to be used as the EntityKey.

  1. public class Blog
  2.   {
  3.     [Key]
  4.     public int PrimaryTrackingKey { get; set; }
  5.     public string Title { get; set; }
  6.     public string BloggerName { get; set;}
  7.     public virtual ICollection<Post> Posts { get; set; }
  8.   }

If you are using code first’s database generation feature, the Blog table will have a primary key column named PrimaryTrackingKey which is also defined as an IdentityKey by default.


Required

The Required annotation tells EF that a particular property is required.

Adding Required to the Title property will force EF and MVC to ensure that the property has data in it.

  1. [Required]
  2. public string Title { get; set; }

With no additional no code or markup changes in the application, my existing MVC application will perform client side validation, even dynamically building a message using the property and annotation names.


Figure 1

With MVC’s client-side validation feature turned off, you will get the same response, except that it will be a result of server-side validation. Entity Framework will perform the validation on the Required annotation and return the error to MVC which will display the message.

The Required attribute will also affect the generated database by making the mapped property non-nullable. Notice that the Title field has changed to “not null”.


MaxLength and MinLength

The MaxLength and MinLength attributes allow you to specify additional property validations, just as you did with Required.

Here is the BloggerName with length requirements. The example also demonstrates how to combine attributes.

  1. [MaxLength(10),MinLength(5)]
  2.       public string BloggerName { get; set; }

The MaxLength annotation will impact the database by setting the property’s length to 10. Prior to that, the default length was 128 as is the length of Title.


MVC 3 client-side annotation and EF 4.1 server-side annotation will both honor this validation, again dynamically building an error message: “The field BloggerName must be a string or array type with a maximum length of '10'.” That message is a little long. Many annotations let you specify an error message with the ErrorMessage attribute.

  1. [MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
  2. public string BloggerName { get; set; }

You can also specify ErrorMessage in the Required annotation.


Figure 2

NotMapped

Code first convention dictates that every property is represented in the database. But this isn’t always the case in your applications. For example you might have a property in the Blog class that creates a code based on the Title and BloggerName fields. That property can be created dynamically and does not need to be stored. You can mark any properties that do not map to the database with the NotMapped annotation such as this BlogCode property.

  1. [NotMapped]
  2.         public string BlogCode{
  3.             get{
  4.                 return Title.Substring(01) + ":" + BloggerName.Substring(01);
  5.             }
  6.         }

ComplexType

It’s not uncommon to describe your domain entities across a set of classes and then layer those classes to describe a complete entity. For example, you may add a class called BlogDetails to your model.

  1. public class BlogDetails
  2.     {
  3.         public DateTime? DateCreated { get; set; }
  4.         [MaxLength(250)]
  5.         public string Description { get; set; }
  6.     }

Notice that BlogDetails does not have any type of key property. In domain driven design, BlogDetails is referred to as a value object. Entity Framework refers to value objects as complex types.  Complex types cannot be tracked on their own.

However as a property in the Blog class, BlogDetails it will be tracked as part of a Blog object. In order for code first to recognize this, you must mark the BlogDetails class as a ComplexType.

  1. [ComplexType]
  2.     public class BlogDetails
  3.     {
  4.         public DateTime? DateCreated { get; set; }
  5.         [MaxLength(250)]
  6.         public string Description { get; set; }
  7.     }

Now you can add a property in the Blog class to represent the BlogDetails for that blog.

    public BlogDetails BlogDetail { get; set; }

In the database, the Blog table will contain all of the properties of the blog include the properties contained in its BlogDetail property. By default, each one is preceded with the name of the complex type, BlogDetail, as you can see in Figure 3.


Figure 3: The Blogs database table containing columns that map to a complex type

Another interesting note is that although the DateCreated property was defined as a non-nullable DateTime in the class, the relevant database field is nullable. You must use the Required annotation if you wish to affect the database schema.

ConcurrencyCheck

The ConcurrencyCheck annotation allows you to flag one or more properties to be used for concurrency checking in the database when a user edits or deletes an entity. If you've been working with Entity Data Model, this aligns with setting a property's ConcurrencyMode to Fixed.

Let’s see how ConcurrencyCheck works by adding it to the BloggerName property.

  1. [ConcurrencyCheck,
  2.  MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),
  3.  MinLength(5)]
  4. public string BloggerName { get; set; }

Concurrency checking depends on having access to the original value of the property. If the context instance that originally queried for a blog stays in memory, it will retain knowledge of the original values of the blog along with its current values. But in a disconnected application, such as the MVC application being used, you’ll need to remind the context of that original value.

You can do that using this db.entry property method before calling SaveChanges. In the following Edit postback method of the sample MVC application, the UI has returned the original value of the blogger name in the originalName parameter. Then it is applied to the DbContext’s tracking entry by setting the Property’s OriginalValue as you can see in the line of code just before SaveChanges is called.

  1. [HttpPost]
  2.         public ActionResult Edit(int id, string originalName, Blog blog)
  3.         {
  4.             try
  5.             {
  6.                 db.Entry(blog).State = System.Data.EntityState.Modified;
  7.                 db.Entry(blog).Property(b => b.BloggerName).OriginalValue = originalName;
  8.                 db.SaveChanges();
  9.                 return RedirectToAction("Index");
  10.             }
  11.             catch
  12.             {
  13.                 return View();
  14.             }
  15.         }

When SaveChanges is called, because of the ConcurrencyCheck annotation on the BloggerName field, the original value of that property will be used in the update. The command will attempt to locate the correct row by filtering not only on the key value but also on the original value of BloggerName.  Here are the critical parts of the UPDATE command sent to the database, where you can see the command will update the row that has a PrimaryTrackingKey is 1 and a BloggerName of “Julie” which was the original value when that blog was retrieved from the database.

  1. where (([PrimaryTrackingKey] = @4) and ([BloggerName] = @5))
  2. @4=1,@5=N'Julie'

If someone has changed the blogger name for that blog in the meantime, this update will fail and you’ll get a DbUpdateConcurrencyException that you'll need to handle.

TimeStamp

It's more common to use rowversion or timestamp fields for concurrency checking. But rather than using the ConcurrencyCheck annotation, you can use the more specific TimeStamp annotation as long as the type of the property is byte array. Code first will treat Timestamp properties the same as ConcurrencyCheck properties, but it will also ensure that the database field that code first generates is non-nullable. You can only have one timestamp property in a given class.

Adding the following property to the Blog class:

  1. [Timestamp]
  2.         public Byte[] TimeStamp { get; set; }

results in code first creating a non-nullable timestamp column in the database table.


Table and Column

You can also use code first with an existing database. But it's not always the case that the names of the classes and properties in your domain match the names of the tables and columns in your database.

My class is named Blog and by convention, code first presumes this will map to a table named Blogs. If that's not the case you can specify the name of the table with the Table attribute. Here for example, the annotation is specifying that the table name is InternalBlogs.

  1. [Table("InternalBlogs")]
  2.    public class Blog

You can also use the Table attribute when code first is creating the database for you if you simply want the table name to be different than the convention.

The Column annotation is a more adept in specifying the attributes of a mapped column. You can stipulate a name, data type or even the order in which a column appears in the table. Here is an example of the Column attribute.

  1. [Column(“BlogDescription", TypeName="ntext")]
  2.    public String Description {get;set;}

Don’t confuse Column’s TypeName attribute with the DataType DataAnnotation. DataType is an annotation used for the UI and is ignored by code first.

Here is the table after it’s been regenerated. The table name has changed to InternalBlogs and Description column from the complex type is now BlogDescription. Because the name was specified in the annotation, code first will not use the convention of starting the column name with the name of the complex type.


DatabaseGenerated

An important database features is the ability to have computed properties. If you're mapping your code first classes to tables that contain computed properties, you don't want Entity Framework to try to update those columns. But you do want EF to return those values from the database after you've inserted or updated data. You can use the DatabaseGenerated annotation to flag those properties in your class along with the Computed enum. Other enums are None and Identity.

  1. [DatabaseGenerated(DatabaseGenerationOption.Computed)]
  2.      public DateTime DateCreated { get; set; }

You can use database generated on byte or timestamp columns when code first is generating the database, otherwise you should only use this when pointing to existing databases because code first won't be able to determine the formula for the computed column.

You read above that by default, a key property will become an identity key in the database. That would be the same as setting DatabaseGenerated to DatabaseGenerationOption.Identity. If you do not want it to be an identity key, you can set the value to DatabaseGenerationOption.None.

Relationship Attributes: InverseProperty and ForeignKey

Code first convention will take care of the most common relationships in your model, but there are some cases where it needs help.

Changing the name of the key property in the Blog class created a problem with its relationship to Post. 

When generating the database, code first sees the BlogId property in the Post class and recognizes it, by the convention that it matches a class name plus “Id”, as a foreign key to the Blog class. But there is no BlogId property in the blog class. The solution for this is to create a navigation property in the Post and use the Foreign DataAnnotation to help code first understand how to build the relationship between the two classes —using the Post.BlogId property — as well as how to specify constraints in the database.

  1. public class Post
  2.     {
  3.         public int Id { get; set; }
  4.         public string Title { get; set; }
  5.         public DateTime DateCreated { get; set; }
  6.         public string Content { get; set; }
  7.         public int BlogId { get; set; }
  8.         [ForeignKey("BlogId")]
  9.         public Blog Blog { get; set; }
  10.         public ICollection<Comment> Comments { get; set; }
  11.     }

Figure 4 shows the constraint in the database that creates a relationship between InternalBlogs.PrimaryTrackingKey and Posts.BlogId. 


Figure 4

The InverseProperty is used when you have multiple relationships between classes.

In the Post class, you may want to keep track of who wrote a blog post as well as who edited it. Here are two new navigation properties for the Post class.

  1. public Person CreatedBy { get; set; }
  2.         public Person UpdatedBy { get; set; }

You’ll also need to add in the Person class referenced by these properties. The Person class has navigation properties back to the Post, one for all of the posts written by the person and one for all of the posts updated by that person.

  1. public class Person
  2.     {
  3.         public int Id { get; set; }
  4.         public string Name { get; set; }
  5.         public List<Post> PostsWritten { get; set; }
  6.         public List<Post> PostsUpdated { get; set; }
  7.     }

Code first is not able to match up the properties in the two classes on its own. The database table for Posts should have one foreign key for the CreatedBy person and one for the UpdatedBy person but code first will create four will foreign key properties: Person_Id, Person_Id1, CreatedBy_Id and UpdatedBy_Id.


To fix these problems, you can use the InverseProperty annotation to specify the alignment of the properties.

  1. [InverseProperty("CreatedBy")]
  2.         public List<Post> PostsWritten { get; set; }
  3.         [InverseProperty("UpdatedBy")]
  4.         public List<Post> PostsUpdated { get; set; }

Because the PostsWritten property in Person knows that this refers to the Post type, it will build the relationship to Post.CreatedBy. Similarly, PostsUpdated will be connected to Post.UpdatedBy. And code first will not create the extra foreign keys.


Summary

DataAnnotations not only let you describe client and server side validation in your code first classes, but they also allow you to enhance and even correct the assumptions that code first will make about your classes based on its conventions. With DataAnnotations you can not only drive database schema generation, but you can also map your code first classes to a pre-existing database.

While they are very flexible, keep in mind that DataAnnotations provide only the most commonly needed configuration changes you can make on your code first classes. To configure your classes for some of the edge cases, you should look to the alternate configuration mechanism, code first’s Fluent API .

About the Author

Julie Lerman is a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other Microsoft .NET topics at user groups and conferences around the world. Julie blogs at thedatafarm.com/blog and is the author of the highly acclaimed book, “Programming Entity Framework” (O’Reilly Media, 2009). Follow her on Twitter.com: julielerman.


【配置属性】—Entity Framework实例详解

Entity Framework Code First的默认行为是使用一系列约定将POCO类映射到表。然而,有时候,不能也不想遵循这些约定,那就需要重写它们。重写默认约定有两种方式:Data Annotations和FluentAPI。Data Annotations在功能上是Fluent API的子集,在一些映射场景下使用Annotations不能达到重写的目的,因此本篇文章中使用Fluent API配置属性。

一、Fluent API配置属性

Code First Fluent API通常情况下是在DbContext的派生类中重写OnModelCreating方法。

1.配置Length

Length用来描述数组的长度,当前包括string和Byte数组。

默认约定Code First对string或byte数组的默认长度约定是max。注意:Sql Server Compact中默认最大数组长度是4000。

重写约定使用HasMaxLength(nn),参数为可空整数。

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>Property(t => t.Name).HasMaxLength(50);

另外关于Length的Fluent API还有下面2个:

IsFixedLength(),配置属性为固定长度。

IsMaxLength(),配置属性为数据库提供程序允许的最大长度。

2.配置Data Type

Data Type表示将.NET类型映射到的数据库的数据类型。

默认约定:列的数据类型由使用的数据库提供程序决定。以SQL Server为例:String->nvarchar(max),Integer->int,Byte[]->varbinary(max),Boolean->bit。

重写约定:使用HasColumnType(“xxx”),下面列子的Photo是byte[]类型,配置映射到image数据类型:

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>Property(t => t.Photo).HasColumnType(<span class="str" style="margin: 0px; padding: 0px;">"image"</span>);

3.配置允许为空和不允许为空

默认约定:主键属性不允许为空,引用类型(String,array)允许为空,值类型(所有的数字类型,Datetime,bool,char)不允许为空,可空的值类型Nullable<T>允许为空。

重写约定:使用IsRequired()配置不允许为空,使用IsOptional()配置允许为空。下面配置Name属性为不为空:

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span> Property(t => t.Name).IsRequired();

4.配置属性到指定列

默认约定:映射到与属性名相同的列。

重写约定:使用Property(t=>t.属性名).HasColumnName(“xxx”)。下面配置Name映射到DepartmentName:

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>Property(t => t.Name).HasColumnName(<span class="str" style="margin: 0px; padding: 0px;">"DepartmentName"</span>);

5.配置主键

默认约定:(1)属性名为ID或Id的默认为主键 

                (2)类名+ID或类名+Id默认为主键  (其中ID或Id的优先级大于类名+ID或类名+Id)

重写约定:使用HasKey(t=>t.属性名)。下面将BlogId配置为主键:

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span> HasKey(t => t.BlogId);

6.配置组合主键

下面的例子将DepartmentId和Name属性组合作为Department类型的主键:

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>HasKey(t => <span class="kwrd" style="margin: 0px; padding: 0px;">new</span> { t.DepartmentId, t.Name });

7.配置Database-Generated

默认约定:整型键:Identity。

重写约定:使用Property(t => t.属性名).HasDatabaseGeneratedOption(DatabaseGeneratedOption)。

DatabaseGeneratedOption枚举包括三个成员:

(1) None:数据库不生成值

(2) Identity:当插入行时,数据库生成值

(3) Computed:当插入或更新行时,数据库生成值

整型默认是Identity,数据库生成值,自动增长,如果不想数据库自动生成值,使用DatabaseGeneratedOption.None。

Guid类型作为主键时,要显示配置为DatabaseGeneratedOption.Identity。

8.配置TimeStamp/RowVersion的乐观并发

默认约定:这个没有默认约定。

配      置:使用Property(t=>t.属性名).IsRowVersion()

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>Property(t => t.RowVersion).IsRowVersion();

9.不配置TimeStamp的乐观并发

有些数据库不支持RowVersion类型,但是又想对数据库的一个或多个字段并发检查,这时可以使用Property(t=>t.属性名).IsConcurrencyToken(),下面的例子将SocialSecurityNumber配置为并发检查。

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>Property(t => t.SocialSecurityNumber).IsConcurrencyToken();

10.配置String属性是否支持Unicode内容

默认约定:默认string是Unicode(在SQL Server中是nvarchar)的。

重写约定:下面的例子使用IsUnicode()方法将Name属性配置为varchar类型的。

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>Property(t => t.Name).IsUnicode(<span class="kwrd" style="margin: 0px; padding: 0px;">false</span>);

11.配置小数的精度和小数位数

默认约定:小数是(18,2)

配      置:使用Property(t=>t.属性名).HasPrecision(n,n)

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span><span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">decimal</span> MilesFromNearestAirport { get; set; }

12.复杂类型

默认复杂类型有以下规则:

(1) 复杂类型没有主键属性 
(2) 复杂类型只能包含原始属性。 
(3)在其他类中使用复杂类型时,必须表示为非集合类型。

使用DbModelBuilder.ComplexType方法显示配置为复杂类型:

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>modelBuilder.ComplexType<Address>();

13.嵌套的复杂类型

嵌套的复杂类型只需显示配置外层,内层自动继承复杂类型的约定。

14.配置复杂类型的属性

配置复杂类型的属性和配置实体属性一样,具体参考下面的实例。

二、配置属性实例

下面直接给出代码,代码上都有注释。注:下面的实体主要是为了演示用

<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   1:  </span>    <span class="rem" style="margin: 0px; padding: 0px;">//实体</span>
   2:      public class Trip
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   3:  </span>    {
   4:          public Guid Identifier { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   5:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> DateTime StartDate { get; set; }
   6:          public DateTime EndDate { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   7:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">decimal</span> CostUSD { get; set; }
   8:          public string Description { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">   9:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">byte</span>[] RowVersion { get; set; }
  10:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  11:  </span> 
  12:      //复杂类型
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  13:  </span>    <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">class</span> Address
  14:      {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  15:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">int</span> AddressId { get; set; }
  16:          public string StreetAddress { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  17:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">string</span> City { get; set; }
  18:          public string State { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  19:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">string</span> ZipCode { get; set; }
  20:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  21:  </span> 
  22:      //复杂类型
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  23:  </span>    <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">class</span> PersonalInfo
  24:      {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  25:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> Measurement Weight { get; set; }
  26:          public Measurement Height { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  27:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">string</span> DietryRestrictions { get; set; }
  28:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  29:  </span> 
  30:      //复杂类型
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  31:  </span>    <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">class</span> Measurement
  32:      {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  33:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">decimal</span> Reading { get; set; }
  34:          public string Units { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  35:  </span>    }
  36:   
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  37:  </span>    <span class="rem" style="margin: 0px; padding: 0px;">//实体</span>
  38:      public class Person
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  39:  </span>    {
  40:          public Person()
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  41:  </span>        {
  42:              Address = new Address();
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  43:  </span>            Info = <span class="kwrd" style="margin: 0px; padding: 0px;">new</span> PersonalInfo()
  44:              {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  45:  </span>                Weight = <span class="kwrd" style="margin: 0px; padding: 0px;">new</span> Measurement(),
  46:                  Height = new Measurement()
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  47:  </span>            };
  48:          }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  49:  </span> 
  50:          public int PersonId { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  51:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">int</span> SocialSecurityNumber { get; set; }
  52:          public string FirstName { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  53:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">string</span> LastName { get; set; }
  54:          public Address Address { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  55:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">byte</span>[] Photo { get; set; }
  56:          public PersonalInfo Info { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  57:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">byte</span>[] RowVersion { get; set; }
  58:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  59:  </span> 
  60:      //对实体Trip的配置,继承自EntityTypeConfiguration<T>
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  61:  </span>    <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">class</span> TripConfiguration : EntityTypeConfiguration<Trip>
  62:      {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  63:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> TripConfiguration()
  64:          {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  65:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置Identifier映射到TripId列,并设为主键,且默认值为newid()</span>
  66:              HasKey(t => t.Identifier).Property(t => t.Identifier).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("TripId");
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  67:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置CostUSD的精度为20,小数位数为3</span>
  68:              Property(t => t.CostUSD).HasPrecision(20, 3);
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  69:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置Description的长度为500</span>
  70:              Property(t => t.Description).HasMaxLength(500);
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  71:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置RowVersion乐观并发检查</span>
  72:              Property(t => t.RowVersion).IsRowVersion();
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  73:  </span>        }
  74:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  75:  </span> 
  76:      //对实体Person的配置,继承自EntityTypeConfiguration<T>
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  77:  </span>    <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">class</span> PersonConfiguration : EntityTypeConfiguration<Person>
  78:      {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  79:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> PersonConfiguration()
  80:          {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  81:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置SocialSecurityNumber不允许为空且乐观并发检查</span>
  82:              Property(t => t.SocialSecurityNumber).IsRequired().IsConcurrencyToken();
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  83:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置FirstName不允许为空</span>
  84:              Property(t => t.FirstName).IsRequired();
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  85:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置LastName不允许为空</span>
  86:              Property(t => t.LastName).IsRequired();
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  87:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置Photo映射到数据库的数据类型为image</span>
  88:              Property(t => t.Photo).HasColumnType("image");
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  89:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置RowVersion乐观并发检查</span>
  90:              Property(t => t.RowVersion).IsRowVersion();
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  91:  </span>        }
  92:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  93:  </span> 
  94:      //对复杂类型Address的配置,继承自ComplexTypeConfiguration<T>
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  95:  </span>    <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">class</span> AddressConfiguration : ComplexTypeConfiguration<Address>
  96:      {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  97:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> AddressConfiguration()
  98:          {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;">  99:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置AddressId映射到AddressId列</span>
 100:              Property(t => t.AddressId).HasColumnName("AddressId");
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 101:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置StreetAddress长度为100并映射到StreetAddrress列</span>
 102:              Property(t => t.StreetAddress).HasMaxLength(100).HasColumnName("StreetAddress");
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 103:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置State长度为50并映射到State列</span>
 104:              Property(t => t.State).HasMaxLength(50).HasColumnName("State");
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 105:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置City长度为50并映射到City列</span>
 106:              Property(t => t.City).HasMaxLength(50).HasColumnName("City");
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 107:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置ZipCode映射到ZipCode列,不支持Unicode内容,并设为固定长度为6</span>
 108:              Property(t => t.ZipCode).IsUnicode(false).IsFixedLength().HasMaxLength(6).HasColumnName("ZipCode");
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 109:  </span>        }
 110:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 111:  </span> 
 112:      //对复杂类型PersonalInfo的配置,继承自ComplexTypeConfiguration<T>
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 113:  </span>    <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> <span class="kwrd" style="margin: 0px; padding: 0px;">class</span> PersonalInfoConfiguration : ComplexTypeConfiguration<PersonalInfo>
 114:      {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 115:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> PersonalInfoConfiguration()
 116:          {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 117:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//配置DietryRestrictions长度为100</span>
 118:              Property(t => t.DietryRestrictions).HasMaxLength(100);
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 119:  </span>        }
 120:      }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 121:  </span> 
 122:      public class BreakAwayContext : DbContext
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 123:  </span>    {
 124:          public DbSet<Trip> Trips { get; set; }
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 125:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">public</span> DbSet<Person> People { get; set; }
 126:   
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 127:  </span>        <span class="kwrd" style="margin: 0px; padding: 0px;">protected</span> <span class="kwrd" style="margin: 0px; padding: 0px;">override</span> <span class="kwrd" style="margin: 0px; padding: 0px;">void</span> OnModelCreating(DbModelBuilder modelBuilder)
 128:          {
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 129:  </span>            <span class="rem" style="margin: 0px; padding: 0px;">//注册配置</span>
 130:              modelBuilder.Configurations.Add(new TripConfiguration());
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 131:  </span>            modelBuilder.Configurations.Add(<span class="kwrd" style="margin: 0px; padding: 0px;">new</span> PersonConfiguration());
 132:              modelBuilder.Configurations.Add(new AddressConfiguration());
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 133:  </span>            modelBuilder.Configurations.Add(<span class="kwrd" style="margin: 0px; padding: 0px;">new</span> PersonalInfoConfiguration());
 134:              base.OnModelCreating(modelBuilder);
<span class="lnum" style="color: rgb(96, 96, 96); margin: 0px; padding: 0px;"> 135:  </span>        }
 136:      }

出处

http://www.cnblogs.com/nianming/

 http://msdn.microsoft.com/en-us/data/gg193958.aspx

0 0
原创粉丝点击