写给新手:零起点学习数据库访问,使用刚刚发布的 EF 4.1。Entity Framework 4.1 EFCodeFirst 入门教程

threenewbee 2011-04-14 10:09:09
加精
注意,本文的源代码已经上传,位于http://download.csdn.net/source/3190793,欢迎下载。

如果你是一个新手,完全对数据库没有概念。你没听说过SQL,搞不清什么是表、什么是字段,也从来没有用过DataSet,DataTable。但是学习过C#语言,那么这篇文章可能会帮助到你。

微软刚刚发布了Entity Framework 4.1,提供了一个重要的新特性,Code First,这个特性的强大之处就在于,有了它,从此你完全不用了解数据库,一样可以轻松存取数据了。

为了开始,你需要安装如下的软件:
Windows 操作系统,为了保证不出意外,推荐安装原版的操作系统,Windows 7以及Windows Server 2008 R2为最佳。我安装的是Windows Server 2003 Enterprise。
首先安装Visual Studio 2010旗舰版,VS2010内置了SQL Server 2008 Express。之后需要安装 .NET Framework 3.5 SP1。然后安装 PowerShell 2.0。你可以选择安装 SQL Server 2008 R2 Management Studio Express。最后安装Visual Studio 2010 Service Pack 1。SP1安装的时间比较长一些,如果CPU不是很好的话。安装完这些,就可以正式开始了。

为了尽可能简单,我们一起来做一个最简单的案例。这个案例包括两个实体:用户组和用户,一个用户组包含N个用户,一个用户只能隶属于一个组。用户组只有一个属性,用户组名,用户有4个字段,用户名、密码、性别、所在城市。给用户组和用户一个 N:1 的关系。最后允许用户注册、登录,查询。

第一步,启动Visual Studio,新建一个Console Application。之所以选择控制台是考虑到一部分人用C#开发Web程序,另一部分人开发WinForms程序。而控制台无疑是最基本和简单的。

首先我们添加对 CodeFirst 的支持:

如图所示打开包管理器控制台。


NuGet是一个工具,可以方便地从社区下载开发库。但是这东西貌似还不完善,GUI版本会出错,所以使用控制台。

在控制台输入 install-package efcodefirst
回车

稍等片刻(在此期间VS会卡死一会儿)

出现
PM> install-package efcodefirst
'EntityFramework (≥ 4.1.10331.0)' not installed. Attempting to retrieve dependency from source...
Done
You are downloading EntityFramework from Microsoft, the license agreement to which is available at http://go.microsoft.com/fwlink/?LinkID=211010. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'EntityFramework 4.1.10331.0'
Successfully installed 'EFCodeFirst 1.1'
Successfully added 'EntityFramework 4.1.10331.0' to ConsoleApplication1
Successfully added 'EFCodeFirst 1.1' to ConsoleApplication1

PM>
表明安装成功。

第二步,我们定义实体类

新建一个代码文件,叫 Model.cs,为了方便起见,我们把所有用到的类都写在这里。

首先我们定义两个实体,代码非常简单:
    class User
{
public string UserName { get; set; }
public string Password { get; set; }
public bool Sex { get; set; }
public string City { get; set; }
}

class UserGroup
{
public int ID { get; set; }
public string GroupName { get; set; }
}


之后我们做一点修饰,让UserName作为关键字,Password不能为空。至于ID,根据约定,默认EF就会把它当作关键字。这个体现了CoC(约定优于配置)的思想,也就是说,只有当你的想法和框架库不同,你才需要额外的工作,否则一切都已经做好了。

为此,首先加上 using System.ComponentModel.DataAnnotations;
我们用到的几个标记都属于Data Annotations。

我们给UserName加上Key标记,给Password加上Required标记。

然后我们来定义关系,非常简单:
在User类里面添加一个UserGroup类型的成员:
UserGroup Group { get; set; }
在UserGroup里面定义一个列表,表示它拥有的用户:
IList<User> Users { get; set; }

现在完整的代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
class User
{
[Key]
public string UserName { get; set; }

[Required]
public string Password { get; set; }

public bool Sex { get; set; }
public string City { get; set; }

public UserGroup Group { get; set; }
}

class UserGroup
{
public int ID { get; set; }
public string GroupName { get; set; }

public IList<User> Users { get; set; }
}
}


现在我们还需要定义一个数据库对象,让EF知道我们程序里面哪些类是实体。

    class UserDB : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroup { get; set; }
}


如果之前使用过Linq To SQL或者EF,那么对DbContext应该不陌生。DbContext对象提供了我们和数据库打交道的入口。

全部定义数据库的代码全部搞好了,至于建立数据库表、关系、结构,这些事情全部都不需要了!你没有听错,这些全部是幕后实现的。

等等,我们还需要一点配置,让EF知道我们用什么数据库服务器,以及在哪里建立数据库。

如图,新建一个配置文件:


在里面加入如下配置:
  <configuration>
<connectionStrings>
<add
name="UserDB"
providerName="System.Data.SqlClient"
connectionString="Server=.\SQLEXPRESS;Database=ConsoleApplication1_db;Trusted_Connection=true;"/>
</connectionStrings>

name和定义的数据库类名一致,至于Server=.\SQLEXPRESS,你可以设置成你自己的SQL Server服务器名。数据库名建议以程序名命名,以免混淆。

做好了这一切,我们就可以试下:

在主程序里面写:

UserDB db = new UserDB();
var ug = new UserGroup()
{
ID = 1,
GroupName = "Admin"
};
db.UserGroup.Add(ug);
db.SaveChanges();


这段代码会产生一个 UserGroup 对象,并且添加到数据库里面。

按 Ctrl + F5 运行。

因为需要初始化数据库,所有有些慢,等程序运行完毕,我们打开数据库看一下:

打开服务器资源管理器窗格,添加一个连接,在服务器名里面输入服务器名(我这里是 .\SQLEXPRESS),选择数据库名。

打开以后如图:


分别查看表关系、User表的定义和UserGroup表的数据:


你已经发现了,EF完美地为我们创建了数据库,并且正确地处理了变量类型、是否为空等属性。更为巧妙的是,它还自动产生了我们需要的 1:N 关系。

最后,我们的程序插入的数据也已经在 UserGroup 里面了。

当Model修改后,数据库结构和模型不匹配了,EF为了防止破坏现有的数据,会阻止程序访问数据库。

在程序调试阶段,我们不希望这样,为此,在Main函数里面我们给指定一个初始化策略:每次创建数据库前都把之前的删掉,从头开始,为此我们在Main()开始处增加如下代码:

Database.SetInitializer<UserDB>(new DropCreateDatabaseAlways<UserDB>());


下面,简单演示下数据库的几种基本操作,增加、删除、修改、查询。

首先我们需要提供一些初始化的数据,为此,我们从DropCreateDatabaseAlways继承一个类,把初始化工作放在里面:

    class DBInitializer : DropCreateDatabaseAlways<UserDB>
{
protected override void Seed(UserDB context)
{
base.Seed(context);

//添加用户组
var uglist = new List<UserGroup>()
{
new UserGroup()
{
GroupName = "Admin"
},
new UserGroup()
{
GroupName = "User"
},
new UserGroup()
{
GroupName = "Guest"
}
};
uglist.ForEach(ug => context.UserGroup.Add(ug));
//上面的 Lambda 表达式等同下面的语句。
//foreach (var ug in uglist)
//{
// context.UserGroup.Add(ug);
//}
context.SaveChanges();

//添加管理员
UserGroup ugadmin = (from x in context.UserGroup
where x.GroupName == "Admin" select x).SingleOrDefault();
if (ugadmin != null)
{
var adminuserlist = new List<User>()
{
new User()
{
UserName = "张三",
City = "北京",
Password = "123456",
Sex = true,
Group = ugadmin
},
new User()
{
UserName = "李四",
City = "天津",
Password = "123456",
Sex = true,
Group = ugadmin
}
};
adminuserlist.ForEach(adminuser => context.Users.Add(adminuser));
}

//添加用户
UserGroup uguser = (from x in context.UserGroup
where x.GroupName == "User"
select x).SingleOrDefault();
if (uguser != null)
{
var userlist = new List<User>()
{
new User()
{
UserName = "小红",
City = "上海",
Password = "xiaohong123",
Sex = false,
Group = uguser
},
new User()
{
UserName = "大军",
City = "唐山",
Password = "123456",
Sex = true,
Group = uguser
},
new User()
{
UserName = "丽丽",
City = "沧州",
Password = "88888888",
Sex = false,
Group = uguser
}
};
userlist.ForEach(user => context.Users.Add(user));
}
context.SaveChanges();
}
}


大家知道为什么中断了那么就没有往下写么?因为我也在摸索。

我发现导航属性(也就是User.Group和UserGroup.Users)没有办法正确获取数据。仔细检查代码,发现了原因。

切记要将导航属性标记为virtual的。

public virtual UserGroup Group { get; set; }

public virtual IList<User> Users { get; set; }

为了方便起见,我给两个实体类重写了 ToString() 方法。

完整的代码如下:
    class User
{
[Key]
public string UserName { get; set; }

[Required]
public string Password { get; set; }

public bool Sex { get; set; }
public string City { get; set; }

public virtual UserGroup Group { get; set; }

public override string ToString()
{
return string.Format("用户名:{0}, 城市:{1}, 所属组:{2}, 性别:{3}.",
UserName, City, Group, Sex == false ? "女" : "男");
}
}

class UserGroup
{
public int ID { get; set; }
public string GroupName { get; set; }

public virtual IList<User> Users { get; set; }

public override string ToString()
{
return string.Format("用户组名:{0}.",
GroupName);
}
}


一切就绪,下面正式进行数据库操作。

为了简单起见,所以的操作均硬编码到方法内,没有从键盘输入,除了登录以外。
...全文
23949 264 打赏 收藏 转发到动态 举报
写回复
用AI写文章
264 条回复
切换为时间正序
请发表友善的回复…
发表回复
skevil 2013-10-19
  • 打赏
  • 举报
回复
学习了,多谢版主啦
Regan-lin 2013-10-19
  • 打赏
  • 举报
回复
proer9988 2013-10-19
  • 打赏
  • 举报
回复
跟风的玩意儿,还是传统的方法可靠。
kamYee 2013-05-27
  • 打赏
  • 举报
回复
汇智联创 2013-05-27
  • 打赏
  • 举报
回复
long68695259 2013-05-27
  • 打赏
  • 举报
回复
楼主很好和你强大
Mr_XHK 2013-05-25
  • 打赏
  • 举报
回复
看了这个http://www.cnblogs.com/mecity/archive/2011/07/07/2099598.html才明白了...
u010833403 2013-05-25
  • 打赏
  • 举报
回复
非常不错
Mr_XHK 2013-05-25
  • 打赏
  • 举报
回复
虽然楼主写了很多代码,但是我还是不知道安装efcodefirst的有什么用,...望楼主别见怪啊,真的新手
kingknght 2013-05-24
  • 打赏
  • 举报
回复
还一直在用ADO连接,LINQ都还没用过,是不是落伍了啊
flashrhx2010 2013-05-24
  • 打赏
  • 举报
回复
不错,新手学习下
u010820966 2013-05-24
  • 打赏
  • 举报
回复
顶一下,我们的框架还用的是ORM,读写操作数据库也不再是直接像传统那样写sql,而是操作实体对象了,我也贴下代码
 //审核
                if (IsCheck)
                {
                    //更新即时库存表数据
                    EntityObjectList eoWHDetail = eo.GetChildEntityObjects(EntityIDEnum.CGRKDetail);//明细物料

                    EntityObjectFactory eof = EntityObjectFactory.GetInstance(this.Context, EntityIDEnum.NowKucun);//库存

                    foreach (EntityObject eoMat in eoWHDetail)
                    {
                        if (eoMat != null)
                        {

                            EntityObject eoMalUpdate = eof.FindFirst("MaterialID={0} and WareNewID={1} and jiliangdanweiID={2} ", eoMat.GetProperty("MaterialID"), eo.GetProperty("WareNewID").ToString(), eoMat.GetRelatedObject("MaterialID").GetProperty("JiliangdanweiID").ToString());
                            decimal currentQty = 0m;
                            decimal currentAmount = 0m;
                                if (eoMalUpdate != null)
                                {
                                    //如果不是第一次入库,也就是即时库存表里面有这个物料数据,我就得到当前的库存数
                                    currentQty = decimal.Parse(eoMalUpdate.GetProperty("Quantity").ToString());
                                    //修改库存数
                                    eoMalUpdate.SetProperty("Quantity", currentQty + decimal.Parse(eoMat.GetProperty("Many").ToString()));


                                    //修改库存单价
                                    eoMalUpdate.SetProperty("Amount", currentAmount + decimal.Parse(eoMat.GetProperty("TotalMoney").ToString()));
                                    if ((currentQty + decimal.Parse(eoMat.GetProperty("Many").ToString())) != 0m)
                                        eoMalUpdate.SetProperty("Danjia", (currentAmount + decimal.Parse(eoMat.GetProperty("TotalMoney").ToString())) / (currentQty + decimal.Parse(eoMat.GetProperty("Many").ToString())));
  


                                }
                                else
                                {
                                    //该物料第一次入库
                                    EntityObject eoMalUpdateNew = eof.CreateObject();
                                    eoMalUpdateNew.SetProperty("MaterialID", eoMat.GetProperty("MaterialID"));
                                    eoMalUpdateNew.SetProperty("WareNewID", eo.GetProperty("WareNewID"));
                                    eoMalUpdateNew.SetProperty("Quantity", eoMat.GetProperty("Many"));
                                    eoMalUpdateNew.SetProperty("Danjia", eoMat.GetProperty("PerMoney"));
                                    eoMalUpdateNew.SetProperty("Amount", eoMat.GetProperty("TotalMoney"));
                                    eoMalUpdateNew.SetProperty("JiliangdanweiID", eoMat.GetRelatedObject("MaterialID").GetProperty("JiliangdanweiID"));

                                }
                             
                        }
                    }
                    this.Context.SaveChanges();
                    return true;
                }
这样让编程纯面向对象,可以让开发效率倍增,开发周期为原来的1/3,有兴趣的朋友也可以去我们官网下载我们的框架体验体验。
whqjj2006 2013-05-24
  • 打赏
  • 举报
回复
学习了
yufeng219 2013-05-24
  • 打赏
  • 举报
回复
没有过,学习
hyde100 2013-05-24
  • 打赏
  • 举报
回复
好好看看。。。
stiff_neck 2013-05-24
  • 打赏
  • 举报
回复
Mark 稍后再看。
  • 打赏
  • 举报
回复
先收藏了 现在刚开始学C#
ktei2008 2013-05-09
  • 打赏
  • 举报
回复
引用 244 楼 sp1234 的回复:
[quote=引用 218 楼 ktei2008 的回复:] 用了一段时间,后来也发现这些弊端了……全改成ADO.NET了……
晕!你用筷子吃饭,就敢保证一辈子没有几次用手抓饭么? 同样道理,你用一个高效优雅的开发方式,并不跟少量使用ADO.NET矛盾。那种总是以为捡起西瓜就必须全盘扔掉玉米的人,肯定会对新东西学不进去甚至抱怨的![/quote] 用什么得具体问题具体分析。我并没有全盘否定entity framework的意思,我只是针对我们做过的某一个项目说的而已。对新人来说,学什么东西都一样,反正都不会。
  • 打赏
  • 举报
回复
引用 196 楼 Zhang_billy 的回复:
hi All, 我发现以后做程序员越来越简单了,只要你能想到,什么都可以由后台执行了。。。
要相信,随着水平普遍提高,只有笨蛋老板才会雇佣“南郭先生一样的程序员”的!
  • 打赏
  • 举报
回复
引用 218 楼 ktei2008 的回复:
用了一段时间,后来也发现这些弊端了……全改成ADO.NET了……
晕!你用筷子吃饭,就敢保证一辈子没有几次用手抓饭么? 同样道理,你用一个高效优雅的开发方式,并不跟少量使用ADO.NET矛盾。那种总是以为捡起西瓜就必须全盘扔掉玉米的人,肯定会对新东西学不进去甚至抱怨的!
加载更多回复(154)

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧