ADO.NET Entity Framework 的新功能:永续保存无知对象。可以说是 Entity Framework 划时代的新功能,颠覆一般的数据组件/DAL 与数据库间的交互方式。
前一篇文章介绍了 ADO.NET Entity Framework 的模型优先设计 (Model First Design) 功能,有没有觉得 .NET Framework 4.0 中的 ADO.NET Entity Framework 进步了很多呢?如果你这样就满足了,那接下来的东西你看了可能会像某啤酒广告的女主角们那样的尖叫~
一般在设计数据类的时候,大多都是以一个纯类 (轻量级类,只声明必要的属性代表字段,并没有和其他对象交互),包含几个只针对数据做处理的属性或方法等等,若要使用它和数据库连结时,只能在里面做一些 Property Mapping,并且另外撰写数据存取的 ADO.NET 程序,这个动作只要是当过应用程序开发人员,就会觉得如呼吸般自然 … 长久以来都是如此,没有一次例外。但 ADO.NET Entity Framework 2.0 试图将这个局面颠覆过来,开发人员可以直接使用纯类对象来生成数据结构,并且由 Entity Framework 代为建立数据库以及表格,这个方式可是以前 Visual Studio 开发人员前所未见的。这个方法在 Java 阵营中已经有一些实践品,而在 Java 阵营中对轻量级的数据对象一个特别的名词:POJO (Plain-old Java Object),在 .NET 阵营也有一个相对的名词,叫 POCO (Plain-old CLR Object),用途和 POJO 差不多,但微软给了一个笔者觉得很难翻的名词: Persistence-Ignorant Object,若直接翻译会变成 “保存无知型对象”,蛮拗口的,在官方翻译未出来前,就暂时先翻成 “无差别保存对象” (编按:在 "软件构筑美学" 一书中,将此词翻译为 "永续保存无知",笔者认为这个词翻的还不错,所以引用) 好了,因为 Persistence-Ignorant Object 具有不必事先在 DBMS 中建立实例数据库,就可以利用 Entity Framework 的 DDL Generation 功能将 POCO 对象结构转换成实例数据结构以存入数据库中的能力。
笔者认为永续保存无知对象在 ADO.NET Entity Framework 乃至于 ADO.NET 甚至是整个 .NET Framework 的 Data Access 机制而言,都是一项划时代的创新,它将数据存取组件设计的刻板印象完全的改变了,没想到只使用几个属性的声明就可以生成一个完成的数据结构,这可是许多开发人员梦寐以求的功能啊,笔者一开始看到这个功能时也是眼睛一亮,试过以后更觉得它是超级好物啊~
也许这样看起来还是有点抽象,实际走一次例子就知道了。不过在做例子前,请先看看环境是否符合:
A. 已安装 Visual Studio 2010
B. 下载并安装 ADO.NET Entity Framework Feature CTP2 : http://www.microsoft.com/downloads/details.aspx?FamilyID=13FDFCE4-7F92-438F-8058-B5B4041D0F01&displayLang=en
C. 已安装 SQL Server (最好是非 Express 版本,但 Express 版也无妨)
前置作业符合后,请依下列步骤执行:
1. 在 Visual Studio 中先建立一个 AdoEF_PocoExampleLibrary 类库项目,并且加入下列类声明程序 (可同在一个类库文件或是分成个别的文件):
public class Blog {
public Blog() { }
public int ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public User Owner { get; set; }
public ICollection
}
public class Comment
{
public Comment() { }
public int ID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public Person Author { get; set; }
public Post Post { get; set; }
public DateTime Created { get; set; }
public DateTime? Posted { get; set; }
}
public class Person
{
public int ID { get; set; }
public string Firstname { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public ICollection
}
public class Post
{
public Post() { }
public int ID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string PermaUrl { get; set; }
public DateTime Created { get; set; }
public DateTime? Posted { get; set; }
public User Author { get; set; }
public User Poster { get; set; }
public Blog Blog { get; set; }
public ICollection
public int BlogID { get; set; }
}
public class User : Person
{
public string Password { get; set; }
public ICollection
public ICollection
public ICollection
}
2. 在 Visual Studio 中建立一个 AdoEF_PocoExampleClient 主控台项目,先新增一个 BloggingModel 类,并将下列程序加入:
using System.Data.Entity;
using System.Data.EntityClient;
using System.Data.EntityModel;
using System.Data.Objects;
public class BloggingModel : ObjectContext
{
public BloggingModel(EntityConnection connection)
: base(connection)
{
DefaultContainerName = "BloggingModel";
}
public IObjectSet
{
get { return base.CreateObjectSet
}
public IObjectSet
{
get { return base.CreateObjectSet
}
public IObjectSet
{
get { return base.CreateObjectSet
}
public IObjectSet
{
get { return base.CreateObjectSet
}
}
3. 在 AdoEF_PocoExampleClient 项目中加入一个 BlogDemo 类,并将下列程序加入(其中红字为数据库连线字符串,但 initial catalog 可以指定成目前还没建的数据库名称,若你已经有 Blogs 数据库,请将 initial catalog 的名称改一下,否则执行以后,你原有的 Blogs 数据库会被删掉):
using System.Data.Entity;
using System.Data.EntityClient;
using System.Data.EntityModel;
using System.Data.Objects;
using Microsoft.Data.Objects;
class BlogDemo
{
public static void Run()
{
var builder = new ContextBuilder
RegisterConfigurations(builder);
var connection = new SqlConnection("initial catalog=Blogs; integrated security=SSPI");
using (var ctx = builder.Create(connection))
{
if (ctx.DatabaseExists())
ctx.DeleteDatabase();
ctx.CreateDatabase();
var EfDesign =
new AdoEF_PocoExampleLibrary.Blog
{
Name = "EF Design",
Url = "http://blogs.msdn.com/efdesign/",
Owner = new AdoEF_PocoExampleLibrary.User
{
ID = 1,
Firstname = "Johnny",
Surname = "Miller",
EmailAddress = "johnnyM@hotmail.com",
Password = "Viking"
}
};
ctx.Blogs.AddObject(EfDesign);
var post = new AdoEF_PocoExampleLibrary.Post
{
Title = "Hello",
Blog = EfDesign,
PermaUrl = EfDesign.Url + "/2009/08/Hello",
Body = "....",
Author = EfDesign.Owner,
Poster = EfDesign.Owner,
Created = DateTime.Today,
Posted = DateTime.Today,
};
ctx.Posts.AddObject(post);
var comment = new AdoEF_PocoExampleLibrary.Comment
{
Title = "RE:" + post.Title,
Body = "Welcome to the world of blogging Johnny...",
Created = DateTime.Now,
Posted = DateTime.Now,
Post = post,
Author = new AdoEF_PocoExampleLibrary.Person
{
ID = 2,
Firstname = "Vincent",
Surname = "Chase",
EmailAddress = "vinny@hotmail.com",
}
};
ctx.Comments.AddObject(comment);
ctx.SaveChanges();
AdoEF_PocoExampleLibrary.Blog blog = ctx.Blogs.Single();
foreach (var entry in blog.Posts)
{
Console.WriteLine(entry.Title);
Console.WriteLine(entry.Author.Firstname);
}
}
}
static void RegisterConfigurations(ContextBuilder
{
builder.Configurations.Add(new CommentConfiguration());
builder.Configurations.Add(new BlogConfiguration());
builder.Configurations.Add(new PostConfiguration());
builder.Configurations.Add(new PersonConfiguration());
builder.Configurations.Add(new UserConfiguration());
}
}
4. 在前一步的程序的下方,加入下列的程序:
public class CommentConfiguration : EntityConfiguration
{
public CommentConfiguration()
{
Property(c => c.ID).IsIdentity();
Property(c => c.Title).HasMaxLength(103).IsRequired();
Property(c => c.Body).IsRequired();
// 1 to * relationships
Relationship(c => c.Author).IsRequired();
Relationship(c => c.Post).IsRequired();
//Register some inverses
Relationship(c => c.Post).FromProperty(p => p.Comments);
Relationship(c => c.Author).FromProperty(u => u.Comments);
}
}
public class BlogConfiguration : EntityConfiguration
{
public BlogConfiguration()
{
Property(b => b.ID).IsIdentity();
Property(b => b.Name).HasMaxLength(100).IsRequired();
Relationship(b => b.Owner).IsRequired();
//Register some inverses
Relationship(b => b.Owner).FromProperty(u => u.Blogs);
Relationship(b => b.Posts).FromProperty(p => p.Blog);
}
}
public class PostConfiguration : EntityConfiguration
{
public PostConfiguration()
{
// Make the PK store generated
Property(p => p.ID).IsIdentity();
// Convert some '0..1 to *' relationships into '1 to *'
Relationship(p => p.Author).IsRequired();
Relationship(p => p.Blog).IsRequired();
Relationship(p => p.Poster).IsRequired();
// Setup some facets
Property(p => p.Body).IsRequired();
Property(p => p.PermaUrl).HasMaxLength(200);
Property(p => p.Title).HasMaxLength(100);
// Register some Inverses
Relationship(p => p.Author).FromProperty(u => u.AuthoredPosts);
Relationship(p => p.Comments).FromProperty(c => c.Post);
Relationship(p => p.Poster).FromProperty(p => p.PostedPosts);
//BlogID is a FK property and Blog is a navigation property backed by this FK
Relationship(p => p.Blog).FromProperty(b => b.Posts).HasConstraint((p, b) => p.BlogID == b.ID);
}
}
public class PersonConfiguration : EntityConfiguration
{
public PersonConfiguration()
{
Property(p => p.ID).IsIdentity();
Property(p => p.Firstname).HasMaxLength(100);
Property(p => p.Surname).HasMaxLength(100);
Property(p => p.EmailAddress).HasMaxLength(200);
MapHierarchy(
p => new
{
pid = p.ID,
email = p.EmailAddress,
fn = p.Firstname,
ln = p.Surname,
}
).ToTable("People");
}
}
public class UserConfiguration : EntityConfiguration
{
public UserConfiguration()
{
Property(u => u.Password).HasMaxLength(15).IsRequired();
Relationship(u => u.AuthoredPosts).FromProperty(p => p.Author);
Relationship(u => u.PostedPosts).FromProperty(p => p.Poster);
MapHierarchy(
u => EntityMap.Row(
EntityMap.Column(u.ID, " u i d"),
EntityMap.Column(u.Password)
)
).ToTable("Users");
}
}
5. 在 AdoEF_PocoExampleClient 的 Program.cs 中,加入下列的程序:
class Program
{
static void Main(string[] args)
{
BlogDemo.Run();
}
}
6. 若你所使用的数据库是 SQL Server (非 Express 或 Compact Edition),可将 SQL Profiler 先启动并进入监听模式。
7. 按 F5 以调试器执行或按 CTRL+F5 直接执行,正常情况下窗口会出现以后消失。此时请打开 SQL Server Management Studio 并连到本机服务器中,即可以看到新的 Blogs 数据库。
8. 若在前面有启动 SQL Profiler 监听,则可以将它停止后,看看 Entity Framework 做了什么事:
如何,是不是有想要尖叫的感觉呢?有了这样的机制,Entity Framework 更向成熟的 ORM Framework 迈进一大步了。
由于这个功能太大,一回文章介绍不完,因此笔者下回再继续深入介绍这个神奇的功能。
参考数据:
Updated Feature CTP Walkthrough: Code Only for Entity Framework
http://blogs.msdn.com/adonet/archive/2009/11/12/updated-feature-ctp-walkthrough-code-only-for-entity-framework.aspx
范例程序下载:
http://blogs.msdn.com/adonet/attachment/9921786.ashx
原文:大专栏 [VS2010] ADO.NET Entity Framework 新功能:永续保存无知对象 (Persistence-Ignorant Object) Overview