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

精进不休 .NET 4.0 (9) - ADO.NET Entity Framework 4.1 之 Code First

Posted on 2011-05-23 08:30  webabcd  阅读(12871)  评论(57编辑  收藏

[索引页]
[源码下载] 


精进不休 .NET 4.0 (9) - ADO.NET Entity Framework 4.1 之 Code First



作者:webabcd


介绍
ADO.NET Entity Framework 4.1 的新增功能:Code First


示例
Web.config

<?xml version="1.0"?>
<configuration>
    <connectionStrings>
        <!--
            需要将 Persist Security Info 设置为 True,以便保存密码信息
            因为 Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>()); 在判断 Code First 与数据库结构是否一致时需要连接 master 库
        -->
        <add name="MyConnection" providerName="System.Data.SqlClient" connectionString="server=.;database=MyDB;uid=sa;pwd=111111;Persist Security Info=True" />
    </connectionStrings>
</configuration>

 

Global.asax.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;

using System.Data.Entity;
using EF41.CodeFirst;

namespace EF41
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            // 当 Code First 与数据库结构不一致时,删除原数据库,根据 Code First 重新生成新的数据库结构
            Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());

            // 什么都不做
            // Database.SetInitializer<MyContext>(null);
        }
    }
}

 

Category.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace EF41.CodeFirst
{
    public class Category
    {
        public string CategoryId { get; set; }
        public string Name { get; set; }
        public string Comment { get; set; }

        public virtual ICollection<Product> Products { get; set; }
    }
}

 

Product.cs

/*
 * 实体到数据库结构的映射是通过默认的约定来进行的,如果需要修改的话,有两种方式,分别是:Data Annotations 和 Fluent API
 * 以下介绍通过 Data Annotations 来修改实体到数据库结构的映射(通过 Fluent API 来修改实体到数据库结构的映射的方法参见 MyContext.cs 文件)
 *     Table - 指定实体所对应的数据库的表名,不指定则对应的表名为类名的复数形式
 *     Key - 指定是否是主键,不指定则 Code First 会将名为“Id”或“<类名>Id”的字段推断为主键,且如果它的类型是"int"或"long"或"short"的话,则会在数据库中默认注册为 identity 字段。注:主键的推断与大小写无关
 *     DatabaseGenerated - 指定字段的值在数据库中的生成方式
 *         DatabaseGeneratedOption.None - 不做任何处理
 *         DatabaseGeneratedOption.Identity - 标识列
 *         DatabaseGeneratedOption.Computed - 计算列
 *     Required - 指定为必填字段,即指定数据库对应的列不允许为 null 值
 *     MaxLength - 指定字段的最大长度,未指定则为 max
 *     StringLength - 指定字段的长度范围
 *     Column - 指定字段所对应的数据库中的列名,默认情况下数据库中的列名同 Code First 中的字段名
 *     NotMapped - 没有对应关系,即此字段不会在数据库中生成对应的列
 *     Timestamp - 指定对应的数据库中的列的类型为 datetime
 *     ForeignKey - 指定外键的名称,默认情况下与导航属性的主键名称相同的字段会自动被标记为外键
 *     InverseProperty - 指定导航属性的反转属性,默认情况下按实体的互相引用自行推断
 *         所谓“反转属性”,按本例看就是 Product.Category 的反转就是 Category.Products
 *     ComplexType - 复杂类型,如果字段类型为一个实体类,则此字段会被自动标记为复杂类型,被标记为复杂类型的字段为必填字段,参见 Price.cs 文件
 *     Timestamp - 将 Code First 中的类型为 byte[] 的字段对应到数据库中的类型为 timestamp 的列
 *     ConcurrencyCheck - 指定字段为用于乐观并发检查的字段,为了简单,建议同时将此字段也标记为 Timestamp
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.ComponentModel.DataAnnotations;

namespace EF41.CodeFirst
{
    [Table("CategoryProduct")]
    public class Product
    {
        [Key]
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
        public int ProductId { get; set; }

        [Required]
        [MaxLength(128)] // [StringLength(128, MinimumLength = 16)]
        [Column("ProductName")]
        public string Name { get; set; }

        [NotMapped]
        public string Comment { get; set; }

        public DateTime CreateTime { get; set; }

        public Price Price { get; set; }

        [ConcurrencyCheck]
        [Timestamp]
        public byte[] TimeStamp { get; set; } 

        public string CategoryId { get; set; }

        [ForeignKey("CategoryId")]
        [InverseProperty("Products")]
        public virtual Category Category { get; set; }
    }
}

 

Price.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.ComponentModel.DataAnnotations;

namespace EF41.CodeFirst
{
    // 以本例来说,对应到数据库的列将被拆为两个,分别是:Price_Dollar 和 Price_RMB,且均为必填列
    [ComplexType]
    public class Price
    {
        public Price()
        {
            Dollar = null;
            RMB = null;
        }

        public decimal? Dollar { get; set; }
        public decimal? RMB { get; set; }
    }
}

 

MyContext.cs

/*
 * 需要引用 EntityFramework(版本 4.1)
 * 需要引用 System.Data.Entity
 * 需要引用 System.ComponentModel.DataAnnotations
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.Data.Entity;

namespace EF41.CodeFirst
{
    // 创建的 Context 要继承自 DbContext
    public class MyContext : DbContext
    {
        // 构造函数中的参数用于指定 connectionStrings 的 name
        // 默认值为 Context 类的类全名,本例为 EF41.CodeFirst.MyContext
        public MyContext(string connString)
            : base(connString)
        {
            // DbContextConfiguration.LazyLoadingEnabled - 是否启用延迟加载,默认值为 true
            //     true - 延迟加载(Lazy Loading):获取实体时不会加载其导航属性,一旦用到导航属性就会自动加载
            //     false - 直接加载(Eager loading):通过 Include 之类的方法显示加载导航属性,获取实体时会即时加载通过 Include 指定的导航属性
            this.Configuration.LazyLoadingEnabled = true;

            // DbContextConfiguration.AutoDetectChangesEnabled - 是否自动监测变化,默认值为 true
            this.Configuration.AutoDetectChangesEnabled = true;
        }

        // 所有需要关联到 Context 的类都要类似如下代码这样定义
        public DbSet<Category> Categories { get; set; }
        public DbSet<Product> Products { get; set; }

        // 实体到数据库结构的映射是通过默认的约定来进行的,如果需要修改的话,有两种方式,分别是:Data Annotations 和 Fluent API
        // 以下介绍通过 Fluent API 来修改实体到数据库结构的映射(通过 Data Annotations 来修改实体到数据库结构的映射的方法参见 Product.cs 文件)
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Category>()
                .Property(s => s.Name)
                .IsUnicode(false) // 指定对应到数据库的类型为 varchar,IsUnicode(true) 为 nvarchar,默认为 nvarchar
                .IsRequired() // 指定对应到数据库的列为必填列
                .HasMaxLength(64); // 指定对应到数据库的列的最大长度为 64
        }
    }
}

 

Demo.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.Data.Entity;

namespace EF41.CodeFirst
{
    public partial class Demo : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // Code First 中通过 DbContext API 实现增删改查的 Demo
            using (var db = new MyContext("MyConnection"))
            {
                Random random = new Random();

                var category = new Category { CategoryId = Guid.NewGuid().ToString(), Name = "software " + random.Next(1, int.MaxValue) };
                db.Categories.Add(category);

                var product = new Product { Name = "windows " + random.Next(1, int.MaxValue), Category = category, CreateTime = DateTime.Now, Price = new Price() };
                var product2 = new Product { Name = "windows " + random.Next(1, int.MaxValue), Category = category, CreateTime = DateTime.Now, Price = new Price() };
                db.Products.Add(product);
                db.Products.Add(product2);

                int recordsAffected = db.SaveChanges();
                Response.Write("影响到数据库的行数:" + recordsAffected.ToString());


                /*
                 * DbContext API 的一些关键点
                 * 
                 * db.Categories.Find() - 通过传递主键值作为参数查找实体,复合主键就传多个参数
                 * db.Categories.Add() - 把一个新增的实体添加到上下文
                 * db.Categories.Attach() - 把一个已存在的实体添加到上下文
                 * db.Entry(entity).State = System.Data.EntityState.Modified - 修改实体状态
                 * db.Categories.AsNoTracking() - 不被 Context 跟踪,通过 NoTracking 获取的实体,其状态是 Detached 状态。当仅仅是获取数据的时候可以用,有助于提高效率
                 * 属性的相关操作,当属性改变时,会自动监测实体状态,即 IsModified = true
                 *     db.Entry(product).Property(p => p.Name).CurrentValue
                 *     db.Entry(product).Property("Name").CurrentValue
                 * 直接加载(Eager loading)的方法
                 *     db.Categories.Include(p => p.Products.First())
                 *     db.Categories.Include(p => p.Products)
                 *     db.Entry(product).Reference(p => p.Category).Load()
                 * 使用 sql
                 *     db.Categories.SqlQuery("select * from Categories").ToList() // 有实体的情况
                 *     db.Database.SqlQuery<string>("select Name from Categories").ToList(); // 无实体的情况
                 *     db.Database.ExecuteSqlCommand(sql); // 直接执行 sql
                 */
            }
        }
    }
}

 


OK 
[源码下载]