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());
}
}
