1. ObjectContext
ObjectContext 对应 LINQ to SQL 的 DataContext,我们通过它来管理数据库连接、实体对象查询更新等。
public class ObjectContext : IDisposable { // Events
public event EventHandler SavingChanges;
// Methods
public ObjectContext(EntityConnection connection);
public ObjectContext(string connectionString);
private ObjectContext(EntityConnection connection, bool isConnectionConstructor);
protected ObjectContext(EntityConnection connection, string defaultContainerName);
protected ObjectContext(string connectionString, string defaultContainerName);
public void AcceptAllChanges();
public void AddObject(string entitySetName, object entity);
public void ApplyPropertyChanges(string entitySetName, object changed);
public void Attach(IEntityWithKey entity);
public void AttachTo(string entitySetName, object entity);
public EntityKey CreateEntityKey(string entitySetName, object entity);
public ObjectQuery<T> CreateQuery<T>(string queryString, params ObjectParameter[] parameters);
public void DeleteObject(object entity);
public void Detach(object entity);
public void Dispose();
public object GetObjectByKey(EntityKey key);
public void Refresh(RefreshMode refreshMode, IEnumerable collection);
public void Refresh(RefreshMode refreshMode, object entity);
public int SaveChanges();
public int SaveChanges(bool acceptChangesDuringSave);
public bool TryGetObjectByKey(EntityKey key, out object value);
// Properties
public int? CommandTimeout { get; set; }
public DbConnection Connection { get; }
public string DefaultContainerName { get; set; }
public MetadataWorkspace MetadataWorkspace { get; }
public ObjectStateManager ObjectStateManager { get; }
}
public partial class TestEntities : ObjectContext
{ public ObjectQuery<User> User
{
get
{
if ((this._User == null))
{
this._User = base.CreateQuery<User>("[User]");
}
return this._User;
}
}
private ObjectQuery<User> _User;
public void AddToUser(User user)
{ base.AddObject("User", user);
} }
使用演示
using (var context = new TestEntities()) { context.Connection.StateChange += (s, e) => Console.WriteLine("{0} -> {1}", e.OriginalState, e.CurrentState); context.Connection.Disposed += (s, e) => Console.WriteLine("Dispose..."); context.Connection.Open(); }
2. EntityObject
EF 总算是补齐了序列化特性,省得我们自己去折腾了。
[EdmEntityType(NamespaceName = "TestModel", Name = "User")]
[DataContract(IsReference = true)]
[Serializable()]
public partial class User : EntityObject
{
public static User CreateUser(int id, string name)
{
User user = new User();
user.Id = id;
user.Name = name;
return user;
}
[EdmScalarProperty(EntityKeyProperty = true, IsNullable = false)]
[DataMember]
public int Id
{
get
{
return this._Id;
}
set
{
this.OnIdChanging(value);
this.ReportPropertyChanging("Id");
this._Id = StructuralObject.SetValidValue(value);
this.ReportPropertyChanged("Id");
this.OnIdChanged();
}
}
private int _Id;
partial void OnIdChanging(int value);
partial void OnIdChanged();
[EdmScalarProperty(IsNullable = false)]
[DataMember]
public string Name
{
get
{
return this._Name;
}
set
{
this.OnNameChanging(value);
this.ReportPropertyChanging("Name");
this._Name = StructuralObject.SetValidValue(value, false);
this.ReportPropertyChanged("Name");
this.OnNameChanged();
}
}
private string _Name;
partial void OnNameChanging(string value);
partial void OnNameChanged();
[EdmRelationshipNavigationProperty("TestModel", "FK_Order_User", "Order")]
[XmlIgnore]
[SoapIgnore]
[DataMember]
public EntityCollection<Order> Order
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedCollection<Order>("TestModel.FK_Order_User", "Order");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)(this)).RelationshipManager.
InitializeRelatedCollection<Order>("TestModel.FK_Order_User", "Order", value);
}
}
}
}
3. LINQ to Entities
这个最易上手,基本上和 LINQ to SQL 没啥区别。
using (var context = new TestEntities()) { var user = (from u in context.User where u.Name == "user1" select u).FirstOrDefault(); Console.WriteLine(user.Name); var users = from u in context.User where u.Age > 0 select new { u.Id, u.Name }; foreach (var item in users) { Console.WriteLine(item.Name); } }
4. Entity SQL
这是很多 LINQ to SQL 用户所期望的,一种类似 T-SQL 且面向对象查询语言。
using (var context = new TestEntities()) { var sql = "SELECT VALUE u FROM TestEntities.User AS u WHERE u.Name = @name"; var user = context.CreateQuery<User>(sql, new ObjectParameter("name", "user1")); foreach (var item in user)
{ Console.WriteLine(item.Name); } }
这种参数化的查询方式有点像我们熟悉的 DbParameter。
我们也可以查询某几个属性值,但返回 的不是匿名类型,而是 System.Data.Common.DbDataRecord。
using (var context = new TestEntities()) { var sql = "SELECT u.Id, u.Name FROM TestEntities.User AS u WHERE u.Name = @name"; var reader = context.CreateQuery<DbDataRecord>(sql, new ObjectParameter("name", "user1")); foreach (var item in reader) { Console.WriteLine(item["name"]); } }
如果只是返回单个属性,别忘了 "VALUE" 。
var sql = "SELECT VALUE u.Name FROM TestEntities.User AS u WHERE u.Name = @name";
var reader = context.CreateQuery<string>(sql, new ObjectParameter("name", "user1"));
foreach (var item in reader)
{
Console.WriteLine(item);
}
5. Method-Based Syntax
用相关方法实现 Chaining Queries,分为 LINQ Method-Based Queries 和 ObjectQuery's Query Builder Methods 两种。
using (var context = new TestEntities()) { var user = context.User.Where(u => u.Name == "user1").Select(u => new { u.Id, u.Name }).First(); Console.WriteLine(user.Name); // --------------------- var users = context.User. Where("it.Name = @name", new ObjectParameter("name", "user1")).Select("it.Name, it.Age").Top("@num", new ObjectParameter("num", 2)); foreach (var item in users) { Console.WriteLine(item["name"]); } }
ObjectQuery<T> 提供了这些基于 Entity SQL 字符串的查询方式。
public class ObjectQuery<T> : ObjectQuery, ... { public ObjectQuery<DbDataRecord> GroupBy(string keys, string projection, params ...);
public ObjectQuery<T> Include(string path);
public ObjectQuery<T> Intersect(ObjectQuery<T> query);
public ObjectQuery<TResultType> OfType<TResultType>();
public ObjectQuery<T> OrderBy(string keys, params ObjectParameter[] parameters);
public ObjectQuery<DbDataRecord> Select(string projection, params ObjectParameter[] parameters);
public ObjectQuery<TResultType> SelectValue<TResultType>(string projection, params ...);
public ObjectQuery<T> Skip(string keys, string count, params ObjectParameter[] parameters);
IEnumerator<T> IEnumerable<T>.GetEnumerator();
public ObjectQuery<T> Top(string count, params ObjectParameter[] parameters);
public ObjectQuery<T> Union(ObjectQuery<T> query);
public ObjectQuery<T> UnionAll(ObjectQuery<T> query);
public ObjectQuery<T> Where(string predicate, params ObjectParameter[] parameters); private string _name; private const string DefaultName = "it"; public string Name { get; set; } }
6. EntityClient
这种方式非常类似传统 ADO.NET 操作。
using (var conn = new EntityConnection("name=TestEntities")) { conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = "SELECT VALUE u FROM TestEntities.User AS u WHERE u.Name == @name"; cmd.Parameters.Add(new EntityParameter { ParameterName = "name", Value = "user1" }); using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) { while (reader.Read()) { Console.WriteLine(reader["Name"]); } } }
7. Add
注意 ObjectContext 用 ObjectQuery<T> 返回 EntitySet,添加实体对象实际通过 ObjectContext.AddObject() 完成的。
using (var context = new TestEntities()) { var user = new User { Name = "user3", Age = 23 }; context.AddToUser(user); context.SaveChanges(); }
8. Update
using (var context = new TestEntities()) { var user = context.User.Where(u => u.Name == "user1").FirstOrDefault(); user.Age += 2; context.SaveChanges(); }
9. Delete
using (var context = new TestEntities()) { var user = context.User.Where(u => u.Name == "user2").FirstOrDefault();
context.DeleteObject(user); context.SaveChanges(); }
10. Refresh
ObjectContext.Refresh() 允许我们自行决定刷新策略,包括数据库优先或者本地修改优先。
using (var context = new TestEntities()) { var user = context.User.Where(u => u.Name == "user1").FirstOrDefault(); user.Age += 13; var age = user.Age; context.Refresh(RefreshMode.StoreWins, user); Console.WriteLine("{0}, {1}", user.Age, age); }
11. ToTraceString
ObjectContext 并没有提供 LINQ to SQL DataContext.Log 这样的功能,要查看实际生成的 T-SQL 语句,要么借助 SQL Server Sql Profiler 这样的工具,要么使用 ObjectQuery.ToTraceString() 方法。
using (var context = new TestEntities())
{ var users1 = context.User.Where(u => u.Name == "user1"); Console.WriteLine((users1 as ObjectQuery).ToTraceString());
var users2 = from u in context.User where u.Name == "user1" select u; Console.WriteLine((users2 as ObjectQuery).ToTraceString());
var sql = "SELECT VALUE u FROM TestEntities.User AS u WHERE u.Name = 'user1'"; var users3 = context.CreateQuery<User>(sql); Console.WriteLine(users3.ToTraceString()) }
输出:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Age] AS [Age]
FROM [dbo].[User] AS [Extent1]
WHERE N'user1' = [Extent1].[Name]
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Age] AS [Age]
FROM [dbo].[User] AS [Extent1]
WHERE N'user1' = [Extent1].[Name]
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Age] AS [Age]
FROM [dbo].[User] AS [Extent1]
WHERE [Extent1].[Name] = 'user1'
12. Connection.Open
当 执行多个操作时,Context 默认情况下会多次打开和关闭数据库连接,可能会造成一定的性能问题。
using (var context = new TestEntities())
{ context.Connection.StateChange += (s, e) => Console.WriteLine("{0} -> {1}", e.OriginalState, e.CurrentState); context.Connection.Disposed += (s, e) => Console.WriteLine("Dispose..."); var user = context.User.First(); Console.WriteLine(user.Age); var order = context.Order.First(); Console.WriteLine(order.Id); }
输出:
Closed -> Open
Open -> Closed
23
Closed -> Open
Open -> Closed
1
Dispose...
我们可以显示打开数据库连接来避免这样的状况。
using (var context = new TestEntities()) { context.Connection.StateChange += (s, e) => Console.WriteLine("{0} -> {1}", e.OriginalState, e.CurrentState); context.Connection.Disposed += (s, e) => Console.WriteLine("Dispose..."); context.Connection.Open(); var user = context.User.First(); Console.WriteLine(user.Age); var order = context.Order.First(); Console.WriteLine(order.Id); }
输出:
Closed -> Open
23
1
Open -> Closed
Dispose...
13. ExecuteCommand
ObjectContext 并没有提供 ExecuteCommand 之类执行原生 T-SQL 的方法,虽然可以用存储过程映射来解决,但总归是少了些什么。某些时候可能因为性能等原因,需要执行一些特殊的操作,那么下面的代码可能帮上一些忙。
using (var context = new TestEntities()) { var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; var factoryProperty = typeof(EntityConnection).GetProperty("StoreProviderFactory", bindingFlags); var factory = factoryProperty.GetValue(context.Connection, null) as DbProviderFactory; var connStr = (context.Connection as EntityConnection).StoreConnection.ConnectionString; using (var conn = factory.CreateConnection()) { conn.ConnectionString = connStr; conn.Open(); var cmd = factory.CreateCommand(); cmd.Connection = conn; cmd.CommandText = "select count(*) from [user]"; Console.WriteLine(cmd.ExecuteScalar()); } }