Entity Framework中的连接管理

EF框架对数据库的连接提供了一系列的默认行为,通常情况下不需要我们太多的关注。但是,这种封装,降低了灵活性,有时我们需要对数据库连接加以控制。

EF提供了两种方案控制数据库连接:

  • 传递到Context的连接;
  • Database.Connnection.Open();

下面详解。

传递到Context的连接

EF6之前版本

有两个接受Connection的构造方法:

public DbContext(DbConnection existingConnection, bool contextOwnsConnection)
public DbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection)

使用上面两个方法的时候需要注意的限制:
1、如果使用上面任意一个方法,传递了一个已经打开的连接,则会在首次使用EF操作数据库时抛出一个InvalidOperationException异常,表示不能重复打开一个链接;

2、contextOwnsConnection参数指示在DbContext对象Dispose时候是否Dispose底层的数据库连接。无论参数是否设置,在DbContext.Dispose()时都会关闭底层的Connection。所以,如果你有超过一个DbContext使用同一连接,其中任何一个DbContext对象Dispose时都会关闭该连接(类似的,如果你将ADO.NET 和DbContext对象混合使用,在DbContect对象Dispose时也会关闭连接)。

可以通过传递一个关闭的连接,在创建的上下文中仅打开一次连接,且仅执行代码绕过上面第一条限制。如下:

 1 using System.Collections.Generic;
 2 using System.Data.Common;
 3 using System.Data.Entity;
 4 using System.Data.Entity.Infrastructure;
 5 using System.Data.EntityClient;
 6 using System.Linq;
 7
 8 namespace ConnectionManagementExamples
 9 {
10     class ConnectionManagementExampleEF5
11     {
12         public static void TwoDbContextsOneConnection()
13         {
14             using (var context1 = new BloggingContext())
15             {
16                 var conn =
17                     ((EntityConnection)
18                         ((IObjectContextAdapter)context1).ObjectContext.Connection)
19                             .StoreConnection;
20
21                 using (var context2 = new BloggingContext(conn, contextOwnsConnection: false))
22                 {
23                     context2.Database.ExecuteSqlCommand(
24                         @"UPDATE Blogs SET Rating = 5" +
25                         " WHERE Name LIKE ‘%Entity Framework%‘");
26
27                     var query = context1.Posts.Where(p => p.Blog.Rating > 5);
28                     foreach (var post in query)
29                     {
30                         post.Title += "[Cool Blog]";
31                     }
32                     context1.SaveChanges();
33                 }
34             }
35         }
36     }
37 }

第二个限制仅仅意味着你要确保确实要关闭连接后再调用DbContext.Dispose()。

EF6版本及更新版本

EF6和将来的版本的DbContext有两个与以往版本相同重载的构造方法,但是不在要求传递一个关闭的连接了。所以下面代码在EF6及以后版本是正确的:

 1 using System.Collections.Generic;
 2 using System.Data.Entity;
 3 using System.Data.SqlClient;
 4 using System.Linq;
 5 using System.Transactions;
 6
 7 namespace ConnectionManagementExamples
 8 {
 9     class ConnectionManagementExample
10     {
11         public static void PassingAnOpenConnection()
12         {
13             using (var conn = new SqlConnection("{connectionString}"))
14             {
15                 conn.Open();
16
17                 var sqlCommand = new SqlCommand();
18                 sqlCommand.Connection = conn;
19                 sqlCommand.CommandText =
20                     @"UPDATE Blogs SET Rating = 5" +
21                      " WHERE Name LIKE ‘%Entity Framework%‘";
22                 sqlCommand.ExecuteNonQuery();
23
24                 using (var context = new BloggingContext(conn, contextOwnsConnection: false))
25                 {
26                     var query = context.Posts.Where(p => p.Blog.Rating > 5);
27                     foreach (var post in query)
28                     {
29                         post.Title += "[Cool Blog]";
30                     }
31                     context.SaveChanges();
32                 }
33
34                 var sqlCommand2 = new SqlCommand();
35                 sqlCommand2.Connection = conn;
36                 sqlCommand2.CommandText =
37                     @"UPDATE Blogs SET Rating = 7" +
38                      " WHERE Name LIKE ‘%Entity Framework Rocks%‘";
39                 sqlCommand2.ExecuteNonQuery();
40             }
41         }
42     }
43 }

还有,现在contextOwnsConnection参数决定是否要在DbContext对象Dispose时关闭并Dispose连接对象。因此,上面代码,在DbContext对象Dispose时,数据库并未关闭(32行),但在以前的版本在此处会关闭连接。上面代码会在第40行关闭并释放。
       当然,如果你仍然可以让DbContext对象控制连接,把contextOwnsConnection设置为true,或者使用另一个构造方法。

Database.Connnection.Open()

EF6以前的版本

EF5及之前版本ObjectionContext.Connection.State状态更新存在BUG,该值不能正确反映底层连接的状态。例如执行下面代码,获取的状态为Closed,即使其底层确实为Open:

((IObjectContextAdapter)context).ObjectContext.Connection.State

另外,如果你通过调用Database.Connection.Open()打开数据库连接,数据库连接将一直打开,直到执行下次执行一个查询或者任何请求连接的操作(例如SaveChanges)。但是在此之后底层连接将会被关闭。那么Context将会因任意的数据库操作而重新打开连接并在之后重新关闭:

 1 using System;
 2 using System.Data;
 3 using System.Data.Entity;
 4 using System.Data.Entity.Infrastructure;
 5 using System.Data.EntityClient;
 6
 7 namespace ConnectionManagementExamples
 8 {
 9     public class DatabaseOpenConnectionBehaviorEF5
10     {
11         public static void DatabaseOpenConnectionBehavior()
12         {
13             using (var context = new BloggingContext())
14             {
15                 // At this point the underlying store connection is closed
16
17                 context.Database.Connection.Open();
18
19                 // Now the underlying store connection is open
20                 // (though ObjectContext.Connection.State will report closed)
21
22                 var blog = new Blog { /* Blog’s properties */ };
23                 context.Blogs.Add(blog);
24
25                 // The underlying store connection is still open
26
27                 context.SaveChanges();
28
29                 // After SaveChanges() the underlying store connection is closed
30                 // Each SaveChanges() / query etc now opens and immediately closes
31                 // the underlying store connection
32
33                 blog = new Blog { /* Blog’s properties */ };
34                 context.Blogs.Add(blog);
35                 context.SaveChanges();
36             }
37         }
38     }
39 }

EF6及将来版本

在EF6和将来的版本中,框架采用的方案是如果代码通过context.Database.Connection.Open()打开一个数据库连接,有理由相信代码想要自己控制连接的打开和关闭,框架将不再自动关闭连接。

注意:这一特性可能造成数据库连接长时间打开,所以要特别留意,防止连接未及时关闭。

  EF6中修复了ObjectContext.Connection.State状态更新的BUG。

 1 using System;
 2 using System.Data;
 3 using System.Data.Entity;
 4 using System.Data.Entity.Core.EntityClient;
 5 using System.Data.Entity.Infrastructure;
 6
 7 namespace ConnectionManagementExamples
 8 {
 9     internal class DatabaseOpenConnectionBehaviorEF6
10     {
11         public static void DatabaseOpenConnectionBehavior()
12         {
13             using (var context = new BloggingContext())
14             {
15                 // At this point the underlying store connection is closed
16
17                 context.Database.Connection.Open();
18
19                 // Now the underlying store connection is open and the
20                 // ObjectContext.Connection.State correctly reports open too
21
22                 var blog = new Blog { /* Blog’s properties */ };
23                 context.Blogs.Add(blog);
24                 context.SaveChanges();
25
26                 // The underlying store connection remains open for the next operation
27
28                 blog = new Blog { /* Blog’s properties */ };
29                 context.Blogs.Add(blog);
30                 context.SaveChanges();
31
32                 // The underlying store connection is still open
33
34            } // The context is disposed – so now the underlying store connection is closed
35         }
36     }
37 }
时间: 2024-07-28 19:46:40

Entity Framework中的连接管理的相关文章

在Entity Framework 中实现继承关系映射到数据库表

继承关系映射到数据库表中有多种方式: 第一种:TPH(table-per-hiaerachy) 每一层次一张表 (只有一张表) 仅使用名为父类的类型名的一张表,它包含了各个子类的所有属性信息,使用区分列(Disciriminator column)(通常内容为子类的类型名)来区分哪一行表示什么类型的数据. 第二种:TPT(Table-per-type) 每种类型都有一张表(父类及每个子类都有表) 父类.各子类各自都有一张表.父类的表中只有共同的数据,子类表中有子类特定的属性.TPT很像类的继承结

在Entity Framework中重用现有的数据库连接字符串

本文转载:http://www.cnblogs.com/dudu/archive/2011/01/29/entity_framework_connection_string.html Entity Framework使用的连接字符串与ADO.NET是不同的,见下图: 相比于ADO.NET,Entity Framework的连接字符串不仅要存放metadata配置信息,还要存放完整的数据库连接字符串(上图中的"provider connection string"部分). 这样的设计有两

Entity Framework 教程——Entity Framework中的实体类型

Entity Framework中的实体类型 : 在之前的章节中我们介绍过从已有的数据库中创建EDM,它包含数据库中每个表所对应的实体.在EF 5.0/6.0中,存在POCO 实体和动态代理实体两种. POCO Entity (Plain Old CLR Object): POCO类是不依赖任何框架的类型,如同其他正常的一般类型,我们称之为"Plain Old CLR Objects"(这里不知道怎么翻译,普通的CLR对象?古老的CLR对象?大概意思就是没有什么特殊的对象吧). POC

[JavaEE - JPA] 3. Spring Framework中的事务管理

前文讨论了事务划分(Transaction Demarcation)在EJB中是如何实现的,本文继续介绍在Spring Framework中是如何完成事务划分的. 我们已经知道了当采用Container事务类型的时候,事务划分主要有以下两种方案(参考这里): 使用JTA接口在应用中编码完成显式划分 在容器的帮助下完成自动划分 在使用JavaEE的EJB规范时,这两种方案分别被实现为BMT以及CMT,关于BMT和CMT在上一篇文章中有比较详尽的讨论(参考这里). 那么对于Spring Framew

在Linq to sql 和 Entity framework 中使用lambda表达式实现left join

我们知道lambda表达式在Linq to sql 和 Entity framework 中使用join函数可以实现inner join,那么怎么才能在lambda表达式中实现left join呢?秘诀就是在join后面加上一个函数DefaultIfEmpty函数,实际上这个函数在linq中貌似也只有将inner join转换为left join的作用,示例如下 var joinResult = DB.Table1s.Join(DB.Table2s, a => a.id, b => b.id,

Entity Framework 中Decimal字段长度设置方法

在创建项目DbContext时,重写DbContext.OnModelCreating()方法:然后通过如下方法指定精度 1 protected override void OnModelCreating(DbModelBuilder modelBuilder) 2 { 3 modelBuilder.Entity<Product>().Property(product => product.Price).HasPrecision(18, 12); 4 } Entity Framework

在Entity Framework中使用事务

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 继续为想使用Entity Framework的朋友在前面探路,分享的东西虽然技术含量不高,但都是经过实践检验的. 在Entity Framework中使用事务很简单,将操作放在TransactionScope中,并通过Complete()方法提

解决Entity Framework中DateTime类型字段异常

今天把一个使用了Entity Framework的程序从MySql迁移到SqlServer时,发现运行时报了一个异常: System.Data.SqlClient.SqlException: 从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值. 在网上查找了一下,具体的错误原因是:C#中的DateTime类型比SqlServer中的datetime范围大.SqlServer的datetime有效范围是1753年1月1日到9999年12月31日,如果超出这个范

关于Entity Framework中的Attached报错的完美解决方案终极版

之前发表过一篇文章题为<关于Entity Framework中的Attached报错的完美解决方案>,那篇文章确实能解决单个实体在进行更新.删除时Attached的报错,注意我这里说的单个实体,指的是要更新或删除的实体不包含其它实体(比如导航属性就包含其它实体),也就是简单POCO对象:但如果不是呢?那么那篇文章里的方法在一定程度上不起作用了,仍会报错,我开始也想不明白,明明通过IsAttached函数判断要更新的实体并未Attached,但进行Attaching时但仍然报错说有相同Key,开