对于DbContext的继承是EF的核心之一。它主要是包含了三个方面的内容:
1 如何连接数据库
2 确定上下文(code first还是Database First等)
3 额外的高级设置
当然,最基础的就是如何连接数据库,下面主要说三种方式:
namespace Magic.Unicorn
{
public class UnicornsContext : DbContext
{
public UnicornsContext()
// C# will call base class parameterless constructor by default
{
}
}
}
这种方式程序将会自动建立并且将数据库并名为Magic.Unicorn.UnicornsContext,而数据库德连接是自动寻找本地的SQL Server Express,所以如果你没有安装,自然会报错。
public class UnicornsContext : DbContext
{
public UnicornsContext()
: base("UnicornsDatabase")
{
}
}
这一种遇上变得有不同的地方,但是差别不到,他将会使用UnicornsDatabase作为数据库名。
这是采用的配置文件,比Express可控性强点。如果你的类和上边的name同名,就会自动连接。
public class UnicornsContext : DbContext
{
public UnicornsContext()
: base("UnicornsCEDatabase")
{
}
}
但如果不是同名的话必须要设置,base(“name=UnicornsCEDatabase”)中name=是可选项。
看起来和codefirst很像,不同的是这个你已经有了数据库,也就是说是Database/Model First,因此,数据库连接参数必然要详细点。而类中设置的方法相同,
public class NorthwindContext : DbContext
{
public NorthwindContext()
: base("name=Northwind_Entities")
{
}
}
对于DbContext构造函数,你能做的不只是这些,比如:
1 使用ModelBuilder类来不实例化建立Code First模型
2 直接传递给DbContext一个连接参数
3 使用已经存在的DbConnection对象,并将它传递给DbContext
4 将ObjectContext传递给DbContext
下面就是使用DbSet属性来定义DbContext驱动内容:
public class UnicornsContext : DbContext
{
public DbSet Unicorns { get; set; }
public DbSet
Princesses { get; set; }
public DbSet LadiesInWaiting { get; set; }
public DbSet Castles { get; set; }
}
其中的属性将会自动Map到数据库并且建立相应的表。
不过你可以同样适用IDbSet属性:
public class UnicornsContext : DbContext
{
public IDbSet Unicorns { get; set; }
public IDbSet
Princesses { get; set; }
public IDbSet LadiesInWaiting { get; set; }
public IDbSet Castles { get; set; }
}
这个主要是使用接口,作用就是接口的作用~呜哈哈
如果不想暴露属性,就可以设置为只读:
public class UnicornsContext : DbContext
{
public IDbSet Unicorns
{
get { return Set(); }
}
public IDbSet
Princesses
{
get { return Set
(); }
}
public IDbSet LadiesInWaiting
{
get { return Set(); }
}
public IDbSet Castles
{
get { return Set(); }
}
}
当数据库建立好以后,我们就可以尝试来做一些查询,什么?SQL,好吧,这个不符合本系列的主题,因为我们将会使用一些面向对象的查询方式:
using (var context = new UnicornsContext())
{
// Query for all unicorns with names starting with B
var unicorns = from u in context.Unicorns
where u.Name.StartsWith("B")
select u;
// Query for the unicorn named Binky
var binky = context.Unicorns
.Where(u => u.Name == "Binky")
.FirstOrDefault();
}
这个代码使用的明显是LINQ的语法,你能够这样写的前提是DbSet或是IDbSet继承了IQueryable。这个你不用操心,因为他本来就是继承的。
我们可以同样适用主键做查询,如果你的查询在当前的Context中没有查询到才会下一步转到database进行查询,算不算是减少了对数据库的压力呢?查找和查询是不同的,额~这一点貌似很不通顺,不过有两点需要记住:
1 在实体没有被查询到的时候才会进入数据库
2 Find将返回被标记为Added状态的实体,但是有时候他还没有被更新到数据库
下面就是适用查找的例子:
using (var context = new UnicornsContext())
{
// Will hit the database
var unicorn = context.Unicorns.Find(3);
// Will return the same instance without hitting the database
var unicornAgain = context.Unicorns.Find(3);
context.Unicorns.Add(new Unicorn { Id = -1 });
// Will find the new unicorn even though it does not exist in the database
var newUnicorn = context.Unicorns.Find(-1);
// Will find a castle which has a string primary key
var castle = context.Castles.Find("The EF Castle");
}
在前面我们看到了为LadyInWaiting实体标记了PrincessId以及CastleName属性,这算是复合属性。
下面的查找就相当于是查找PrincessId=3并且CastleName=’…’的实体:
using (var context = new UnicornsContext())
{
var lady = context.LadiesInWaiting.Find(3, "The EF Castle");
}
可能一会好奇什么是实体的状态,下面我们就说明关于实体状态的东西。
一般来说,EF中包含了如下的集中状态:
| Added |
数据库中没有,添加到数据库 |
| Unchanged |
数据库中存有,没有变化 |
| Modified |
数据库中存有,属性值被修改 |
| Deleted |
被上下文跟踪到,数据库中还存有 |
| Detached |
没有被上下文跟踪到 |
在DbContext中我们通常会用到SaveChanges,他在对以下的实体作用时有些不同:
| Unchaged 实体没有被触及 |
| Added实体将会插入数据库,返回以后成为Unchanged实体 |
| Modified实体在数据库中更新成为Unchanged实体 |
| Deleted实体从数据库中删除,然后成为Detached |
下面演示如何添加实体到Context中:
第一种方式,使用的比较普遍:
using (var context = new UnicornsContext())
{
var unicorn = new Unicorn { Name = "Franky", PrincessId = 1};
context.Unicorns.Add(unicorn);
context.SaveChanges();
}
第二种方式就是通过状态来进行:
using (var context = new UnicornsContext())
{
var unicorn = new Unicorn { Name = "Franky", PrincessId = 1};
context.Entry(unicorn).State = EntityState.Added;
context.SaveChanges();
}
附加一个已经存在的实体到上下文的作用是数据库中存在但是没有被上下文跟踪到,就可以使用如下的方式进行:
var existingUnicorn = GetMyExistingUnicorn();
using (var context = new UnicornsContext())
{
context.Unicorns.Attach(existingUnicorn);
// Do some more work...
context.SaveChanges();
}
另一种方式就是:
var existingUnicorn = GetMyExistingUnicorn();
using (var context = new UnicornsContext())
{
context.Entry(existingUnicorn).State = EntityState.Unchanged;
// Do some more work...
context.SaveChanges();
}
而对于存在但是修改过的实体,我们可以使用状态来附加到上下文:
var existingUnicorn = GetMyExistingUnicorn();
using (var context = new UnicornsContext())
{
context.Entry(existingUnicorn).State = EntityState.Modified;
context.SaveChanges();
}
这样实体的属性值都会被自动送到数据库中,但是有一点,他所引用的其他实体将不会被跟踪到,所以需要小心别出问题,造成数据不一致了~
在写程序的时候我们经常会需要判断一个实体是否是新加入或是更新过的,通过EF我们能够利用状态来进行判断进而进行其他相关操作:
public void InsertOrUpdate(DbContext context, Unicorn unicorn)
{
context.Entry(unicorn).State = unicorn.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}