
如何使用自定义配置步骤来设置EF数据库
给你举个例子:
<pre t="code" l="csharp"> static void Main(string[] args)
{
using (MyDBEntities en = new MyDBEntities())
{
var p1 = enPeopleFirst(x =>
xName == "Jim");
ConsoleWriteLine(p1Age);
var p2 = enDatabaseSqlQuery<Person>(
@"select top 1 from person
where name = 'Jim'")
First();
ConsoleWriteLine(p2Age);
//p1 p2是同一个记录
}
ConsoleReadLine();
}
二、查询问题分析
(一) 数据查询应该在哪做
在EF中,面向对象的数据查询主要提供了两种方式:
TEntity DbSet<TEntity>Find(params object[] keyValues):针对主键设计的通过主键查找单个实体,会先在EF的本地数据集Local中进行查询,如果没有,再去数据库中查询。
IQueryable<T>、IEnumerable<T>类型的所有数据查询的扩展方法(由于DbSet<T>继承于IQueryable<T>与IEnumerable<T>),如SingleOrDefault,FirstOrDefault,Where等。其中IQueryable<T>的扩展方法会先收集需求,到最后一步再生成相应的SQL语句进行数据查询;而IEnumerable<T>的扩展方法则是在查询的第一步就生成相应的SQL语句获取数据到内存中,后面的 *** 作都是以内存中的数据为基础进行 *** 作的。
以上两种方式为EF的数据查询提供了极大的自由度,这个自由度是我们在封装的时候需要保持的。但是,在阅读不少人(其中不乏工作了几年的)对EF的封装,设计统一的数据 *** 作接口Repository中关于数据查询的 *** 作中,通常会犯如下几种失误:
设计了很多GetByName,GetByXX,GetByXXX的 *** 作,这些 *** 作通常并不是所有实体都会用到,只是部分实体的部分业务用到,或者是“估计会用到”。
定义了按条件查询的SingleOrDefault,FirstOrDefault,Count,GetByPredicate(predicate)等方法,但是对于条件predicate的类型是使用Expression<Func<TEntity, boo>>还是Func<TEntity, bool>很纠结,最后干脆两个都设计,相当于把IQueryable<T>,IEnumerable<T>的方法再过一遍。
定义了获取全部数据的GetAll()方法,但却使用了IEnumerable<TEntity>类型的返回值,明白的同学都知道,这相当于把整个表的数据都加载到内存中,问题很严重,设计者却不知道。
诸如此类,各种奇葩的查询 *** 作层出不穷,这些 *** 作或者破坏了EF数据查询原有的灵活性,或者画蛇添足。
其实,这么多失误的原因只有一个,设计者忘记了EF是ORM,把EF当作adonet来使用了。只要记着EF是ORM,以上这些功能已经实现了,就不要去重复实现了。那么以上的问题就非常好解决了,只要:
在数据 *** 作Repository接口中把EF的DbSet<TEntity>开放成一个只读的IQueryable<TEntity>类型的属性提供给业务层作为数据查询的数据源
就可以了。这个数据源是只读的,并且类型是IQueryable<T>,就保证了它只能作为数据查询的数据源,而不像开放了DbSet<T>类型那样可以在业务层中调用EF的内部方法进行增、删、改等 *** 作。另外IQueryable<T>类型保持了EF原有的查询自由性与灵活性,简单明了。这个数据集还可以传递到业务层的各个层次,以实现在哪需要数据就在哪查的灵活性。
(二) 循环中的查询陷阱
EF的导航属性是延迟加载的,延迟加载的优点就是不用到不加载,一次只加载必要的数据,这减少了每次加载的数据量,但缺点也不言自明:极大的增加了数据库连接的次数,比如如下这么个简单的需求:
输出每个用户拥有的角色数量
根据这个需求,很容易就写出了如下的代码:
遍历所有用户信息,输出每个用户信息中角色(导航属性)的数量。
上面这段代码逻辑很清晰,看似没有什么问题。我们来分析一下代码的执行过程:
132行,从IOC容器中获取用户仓储接口的实例,这没什么问题。
133行,取出所有用户信息(memberRepositoryEntities),执行SQL如下:
SELECT [Extent1][Id] AS [Id], [Extent1][UserName] AS [UserName], [Extent1][Password] AS [Password], [Extent1][NickName] AS [NickName], [Extent1][Email] AS [Email], [Extent1][IsDeleted] AS [IsDeleted], [Extent1][AddDate] AS [AddDate], [Extent1]1682922372 AS 1682922372, [Extent2][Id] AS [Id1] FROM [dbo][Members] AS [Extent1] LEFT OUTER JOIN [dbo][MemberExtends] AS [Extent2] ON [Extent1][Id] = [Extent2][Member_Id]
虽然EF生成的SQL有些复杂,但还是没什么问题
3 136行,就开始有问题了,每次循环都会连接一次数据库,执行一次如下查询(最后一个1是用户编号):
exec sp_executesql N'SELECT [Extent2][Id] AS [Id], [Extent2][Name] AS [Name], [Extent2][Description] AS [Description], [Extent2][RoleTypeNum] AS [RoleTypeNum], [Extent2][IsDeleted] AS [IsDeleted], [Extent2][AddDate] AS [AddDate], [Extent2]1682922372 AS 1682922372 FROM [dbo][RoleMembers] AS [Extent1] INNER JOIN [dbo][Roles] AS [Extent2] ON [Extent1][Role_Id] = [Extent2][Id] WHERE [Extent1][Member_Id] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
试想,如果有100个用户,就要连接100次数据库,这么一个简单的需求,连接了101次数据库,还不得让数据库疯掉了。
当然,有同学可以要说,这里用了延迟加载才会多了很多连接数据库的次数,你可以立即加载啊,把Role角色一次性加载进来。好吧,我们来看看立即加载:
143行,在取所有用户信息的时候使用Include方法把与用户关联的所有角色信息也一并查询出来了,这样在循环遍历的时候就不会再连接数据库去查询角色信息了。但是如果看到执行的SQL语句,估计你想死的心情都有了。执行的查询如下:
SELECT [Project1][Id] AS [Id], [Project1][UserName] AS [UserName], [Project1][Password] AS [Password], [Project1][NickName] AS [NickName], [Project1][Email] AS [Email], [Project1][IsDeleted] AS [IsDeleted], [Project1][AddDate] AS [AddDate], [Project1]1682922372 AS 1682922372, [Project1][Id1] AS [Id1], [Project1][C1] AS [C1], [Project1][Id2] AS [Id2], [Project1][Name] AS [Name], [Project1][Description] AS [Description], [Project1][RoleTypeNum] AS [RoleTypeNum], [Project1][IsDeleted1] AS [IsDeleted1], [Project1][AddDate1] AS [AddDate1], [Project1][Timestamp1] AS [Timestamp1] FROM ( SELECT [Extent1][Id] AS [Id], [Extent1][UserName] AS [UserName], [Extent1][Password] AS [Password], [Extent1][NickName] AS [NickName], [Extent1][Email] AS [Email], [Extent1][IsDeleted] AS [IsDeleted], [Extent1][AddDate] AS [AddDate], [Extent1]1682922372 AS 1682922372, [Extent2][Id] AS [Id1], [Join2][Id] AS [Id2], [Join2][Name] AS [Name], [Join2][Description] AS [Description], [Join2][RoleTypeNum] AS [RoleTypeNum], [Join2][IsDeleted] AS [IsDeleted1], [Join2][AddDate] AS [AddDate1], [Join2]1682922372 AS [Timestamp1], CASE WHEN ([Join2][Member_Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1] FROM [dbo][Members] AS [Extent1] LEFT OUTER JOIN [dbo][MemberExtends] AS [Extent2] ON [Extent1][Id] = [Extent2][Member_Id] LEFT OUTER JOIN (SELECT [Extent3][Member_Id] AS [Member_Id], [Extent4][Id] AS [Id], [Extent4][Name] AS [Name], [Extent4][Description] AS [Description], [Extent4][RoleTypeNum] AS [RoleTypeNum], [Extent4][IsDeleted] AS [IsDeleted], [Extent4][AddDate] AS [AddDate], [Extent4]1682922372 AS 1682922372 FROM [dbo][RoleMembers] AS [Extent3] INNER JOIN [dbo][Roles] AS [Extent4] ON [Extent4][Id] = [Extent3][Role_Id] ) AS [Join2] ON [Extent1][Id] = [Join2][Member_Id] ) AS [Project1] ORDER BY [Project1][Id] ASC, [Project1][Id1] ASC, [Project1][C1] ASC
(三) 导航属性的查询陷阱
我们再来回顾一下导航属性的长相(以用户信息中的角色信息为例):
可以看到,集合类的导航属性是一个ICollection<T>类型的集合,其实现类可以是通常使用List<T>或者HashSet<T>。用了ICollection<T>,就限定了集合类的导航属性是一个内存集合,只要用到这个导航属性,就必须把集合中的所有数据都加载到内存中,才能进行后续 *** 作。比如上面的例子中,我们的需求只是想知道用户拥有角色的数量,原意只是要执行一下SQL的Count语句即可,却想不到EF是把这个集合加载到内存中(上面的语句,是把当前用户的所有角色信息查询出来),再在内存中进行计数,这无形中是一个很大的资源浪费。比如在一个商城系统中,我们想了解一种商品的销量(productOrdersCount),那就可能把几万条订单信息都加载到内存中,再进行计数,这将是灾难性的资源消耗。
1 使用CodeFirst方式创建数据库
我们新建一个控制台项目,项目中添加两个Model:Author和Blog以及DbContext。 DbContext的添加方式如下:
项目上右键->添加->新建项->ADONET Entity Data Model->Empty Code First model
项目代码如下:
1 //默认生成的数据表名为类名+字母s,这里使用TableAttribute来指定数据表名为T_Authors
2 [Table("T_Authors")]
3 public class Author
4 {
5 public int Id { set; get; }
6 public string Name { set; get; }
7 /
8 此处定义了Blog类型的属性,所以要确保Blog类中至少要有一个表示主键的字段,即public int Id { set; get; }。
否则在生成数据表时会报错:"EntityType 'Blog' has no key defined Define the key for this EntityType
9 Blogs: EntityType: EntitySet 'Blogs' is based on type 'Blog' that has no keys defined"
10 /
11 public virtual ICollection<Blog> Blogs { set; get; }
12 }
13
14 [Table("T_Blogs")]
15 public class Blog
16 {
17 public int Id { set; get; }
18 public string Title { set; get; }
19 public DateTime Time { set; get; }
20 public int AuthorId { set; get; }
21 public virtual Author Author { set; get; }
22 }
23
24 public class MyDbContext: DbContext
25 {
26 public MyDbContext()
27 : base("name=MyDbContext")
28 {
29 }
30 //DbContext会根据配置文件中connectionStrings指定的数据库名称来建立数据库
31 //DbContext根据DbSet属性的类型来创建数据表,这里指定了Author类型的属性,所以会生成T_Authors数据表
32 public virtual DbSet<Author> Authors { set; get; }
33 }
CodeFirst方式会根据配置文件中的配置生成数据库,这里小编使用的是MYSQL数据库,配置文件如下:
1 <!--EF for MYSQL-->
2<entityFramework codeConfigurationType="MySqlDataEntityMySqlEFConfiguration, MySqlDataEntityEF6">
3 <defaultConnectionFactory type="SystemDataEntityInfrastructureSqlConnectionFactory, EntityFramework" />
4 <providers>
5 <provider invariantName="MySqlDataMySqlClient" type="MySqlDataMySqlClientMySqlProviderServices, MySqlDataEntityEF6" />
6 </providers>
7 </entityFramework>
8 <connectionStrings>
9 <add name="MyDbContext" connectionString="server=localhost;port=3306;database=EF;uid=root;password=root" providerName="SystemDataMySqlClient" />
10 </connectionStrings>
PS:小编使用的是EF6和MYSQL数据库,所以要在项目中添加对MysqlDataEntityEF6以及EntityFrameword 60的引用。
到此,我们已经完成生成数据库的工作,接下来在Main方法中写两行代码:
1 using (var db = new ManagerDb())
2 {
3 dbAuthorsAdd(new Author() { Name = "xfh" });
4 dbSaveChanges();
5 }
Entity Framework 应用程序有以下优点:
1、应用程序可以通过更加以应用程序为中心的概念性模型(包括具有继承性、复杂成员和关系的类型)来工作。
2、应用程序不再对特定的数据引擎或存储架构具有硬编码依赖性。
3、可以在不更改应用程序代码的情况下更改概念性模型与特定于存储的架构之间的映射。
4、开发人员可以使用可映射到各种存储架构(可能在不同的数据库管理系统中实现)的一致的应用程序对象模型。
5、多个概念性模型可以映射到同一个存储架构。 · 语言集成查询支持可为查询提供针对概念性模型的编译时语法验证。
扩展资料:
实体框架(Entity Framework)是微软以ADONet为基础开发出来的对象关系映射(ORM)解决方案,它解决了对象持久化问题,将程序员从编写麻烦的SQL语句当中解放出来。
相对于传统的ADO等各种数据库 *** 纵技术来说,微软的ADONet更为先进,它封装了很多底层 *** 作,抽象了接口,针对接口编程,将调用统一化。
EF框架的应用:
首先,实在VS中建立新项,添加新建项—数据—ADONET实体数据模型,然后设置其自己想要的映射的数据集,设置成功后,会生成一些列文件:
如上图所示,这就是我测试用的shoppingBus数据库生成的实体映射。其中有3个重要的类,分别是:
dataModelContexttt下的dataModelContextcs类,这个类是包含的数据库的上下文关系,我当时在看的时候就想到了设计模式策略模式中的context类,我认为它们有着共同之处,都是负责数据间的交互和实现。
DataModeltt下的数据表类,比如这里的就是petcs类和petTypecs类。这里就是相当于具体的实体类,值得特别说明的是,EF生成的实体映射同时包括表关系,主外键的关系等。
参考资料:
百度百科-ADONET Entity Framework
以上就是关于如何使用自定义配置步骤来设置EF数据库全部的内容,包括:如何使用自定义配置步骤来设置EF数据库、MVC4+EFcodefirst中如何处理数据库历史记录的保存和查询、net-EF codefirst 怎么在创建数据库时增加初始数据等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)