博客寫了10篇了~有很多朋友私信問了一些問題,而且很多問題 大家問的都一樣 這里說說這些常見問題的解決辦法.如果大家有更好的解決辦法~也希望分享出來
問題大概為這幾個
一.ef4.1 codeFirst 修改表結構 增加字段等 EF code first需要重新生成庫導致數據丟失的問題.
二.ef4.1 沒有了edmx等復雜的東西 變得簡單 干凈 但如何使用存儲過程,存儲過程可以返回表 可以返回數值 也有可能是執行修改 刪除 增加等 該怎么做?
三.ef4.1 如何使用數據庫視圖?每個視圖都要去建立對應的實體類么?有簡單的方法么?
四.ef4.1 如何執行SQL函數等操作?
五.ef4.1 如何跨數據庫訪問?
六.ef4.1執行連接查詢?什么時候執行左連接? 什么時候執行內連接? ef 根據什么去判斷?
七.新手使用ef4.1 常見的一些報錯信息
八.ef4.1返回datatable 方便做報表等一些復雜操作
其實這些問題是比較簡單的~ 所以此文送給剛用ef4.1的新手~
下面開始一一解決這些問題
一.ef4.1 codeFirst 修改表結構 增加字段等 EF code first需要重新生成庫導致數據丟失的問題
說這個問題前 首先先說下 我使用ef4.1 codefirst的目的. 是因為可以有更純凈的POCO 不再有EDMX這些東西 而不是真正的用 code first 先有代碼 再生成數據庫.所以 我雖然使用
的是codefirst 但是本質依然是數據庫優先.
所以這個被問的很多的問題 解決辦法其實是非常簡單的.只要你的數據庫已經存在了 那么即使你用code first ef 也不會給你去生成數據庫的. 這個時候 你增加表字段 甚至增加表 只要把
實體類也相應的修改 則數據庫里的數據 是不會被清空的.
說下我的開發步驟 先設計數據庫 並建立數據庫=>通過EF工具生成映射和實體類=>開發代碼 當遇到修改時=> 先修改數據庫如添加字段或表等=>再修改實體類=>繼續開發
這樣就不會有重新生成數據的煩惱了 而且項目里也不會出現edmx~
還有 使用EF4.3 的數據遷移功能 也可以完美解決~
二.ef4.1 沒有了edmx等復雜的東西 變得簡單 干凈 但如何使用存儲過程,存儲過程可以返回表 可以返回數值 也有可能是執行修改 刪除 增加等 該怎么做?
說這個問題前 依然先說下我的觀點.個人認為 既然使用orm框架 就應該把業務邏輯等 都放到業務邏輯層 而不應該再使用存儲過程。我更偏重重業務邏輯層 輕存儲過程這樣的開發~
再ef4.0里 添加存儲過程 比較容易 有edmx 調一調 存儲過程就添加上了 但是在ef4.1里 只有干凈的poco 不再有edmx了 改怎么辦呢?尤其是存儲過程可以是查表 查值 或者執行修改刪除.
一個一個來解決
1.執行返回表類型的存儲過程
先上存儲過程 隨手寫的一個最簡單的
Create PROCEDURE [dbo].[ProSelectStu]
@StudentID int
AS
BEGIN
Select Student.* from Enrollment,Student
where Enrollment.StudentID=Student.StudentID
and Enrollment.StudentID=@StudentID
END
GO
執行存儲過程的方法 是用直接執行sql的方式 我在我的文章第九篇 有過詳細的介紹~大家可以先去看下
執行表的存儲過程 其實是非常強大的 延遲加載 等都有體現 博客園的陸老師已經寫了 寫的非常清楚了~我這里就不再寫了 大家可以去他那看下 提供個連接~
2.執行返回值的存儲過程
先上存儲過程
CREATE PROCEDURE [dbo].[ProSelectCount]
@StuId int
AS
BEGIN
select COUNT(*) from Enrollment where StudentID=@StuId
END
一個簡單的查詢數量
這里用sqlQuery 執行訪問 數據庫 因為需要提供返回類型 而我們返回的是int 所以先得到int的類型
3.執行增刪改
CREATE PROCEDURE [dbo].[ProDel]
@stuId int,
@courseId int
AS
BEGIN
DELETE FROM [WLFSchool].[dbo].[Enrollment]
where StudentID=@stuId and CourseID=@courseId
END
這個用的是操作數據庫 返回受影響行數
三.ef4.1 如何使用數據庫視圖?每個視圖都要去建立對應的實體類么?有簡單的方法么?
先說下最傳統的方法 只需把視圖 當成表 建立對應的實體類 然后加到dbcontext 里即可。沒什么難度。
再說一個問題 使用linq 有個非常美妙的功能 投影映射 和C#3.0的 匿名函數 讓我們很多情況 不需要視圖的
from c in classes
from s in students
where c.ClassID == s.ClassID
order by c.CreateTime
select new
{
Name = s.Name,
Age = s.Age,
ClassName = c.ClassName
};
再通過 var result 接受上面的值 這樣我們就不用去數據庫建視圖 不用再建實體類 是不是很省事呢?
如果公司強大的DBA 已經給我們建好了很多視圖 是不是就要一個個去寫實體類呢?如果你使用的是C#4.0 那么可以用動態的 來解決這個問題~
像下面這樣使用 是不是很爽
這個不僅可以查詢視圖 普通的表 只要是SQL語句 都可以自動生成動態類 讓你用~
下面是擴展方法 和 使用Emit 來動態構建 感謝 ASP.NET 韋 給的幫助~~

public static class DatabaseExtensions
{
public static IEnumerable SqlQueryForDynamic(this Database db,
string sql,
params object[] parameters)
{
IDbConnection defaultConn = new System.Data.SqlClient.SqlConnection();
return SqlQueryForDynamicOtherDB(db, sql, defaultConn, parameters);
}
public static IEnumerable SqlQueryForDynamicOtherDB(this Database db,
string sql,
IDbConnection conn,
params object[] parameters)
{
conn.ConnectionString = db.Connection.ConnectionString;
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
IDataReader dataReader = cmd.ExecuteReader();
if (!dataReader.Read())
{
return null; //無結果返回Null
}
#region 構建動態字段
TypeBuilder builder = DatabaseExtensions.CreateTypeBuilder(
"EF_DynamicModelAssembly",
"DynamicModule",
"DynamicType");
int fieldCount = dataReader.FieldCount;
for (int i = 0; i < fieldCount; i++)
{
//dic.Add(i, dataReader.GetName(i));
//Type type = dataReader.GetFieldType(i);
DatabaseExtensions.CreateAutoImplementedProperty(
builder,
dataReader.GetName(i),
dataReader.GetFieldType(i));
}
#endregion
dataReader.Close();
dataReader.Dispose();
cmd.Dispose();
conn.Close();
conn.Dispose();
Type returnType = builder.CreateType();
if (parameters != null)
{
return db.SqlQuery(returnType, sql, parameters);
}
else
{
return db.SqlQuery(returnType, sql);
}
}
public static TypeBuilder CreateTypeBuilder(string assemblyName,
string moduleName,
string typeName)
{
TypeBuilder typeBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName(assemblyName),
AssemblyBuilderAccess.Run).DefineDynamicModule(moduleName).DefineType(typeName,
TypeAttributes.Public);
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
return typeBuilder;
}
public static void CreateAutoImplementedProperty(
TypeBuilder builder,
string propertyName,
Type propertyType)
{
const string PrivateFieldPrefix = "m_";
const string GetterPrefix = "get_";
const string SetterPrefix = "set_";
// Generate the field.
FieldBuilder fieldBuilder = builder.DefineField(
string.Concat(
PrivateFieldPrefix, propertyName),
propertyType,
FieldAttributes.Private);
// Generate the property
PropertyBuilder propertyBuilder = builder.DefineProperty(
propertyName,
System.Reflection.PropertyAttributes.HasDefault,
propertyType, null);
// Property getter and setter attributes.
MethodAttributes propertyMethodAttributes = MethodAttributes.Public
| MethodAttributes.SpecialName
| MethodAttributes.HideBySig;
// Define the getter method.
MethodBuilder getterMethod = builder.DefineMethod(
string.Concat(
GetterPrefix, propertyName),
propertyMethodAttributes,
propertyType,
Type.EmptyTypes);
// Emit the IL code.
// ldarg.0
// ldfld,_field
// ret
ILGenerator getterILCode = getterMethod.GetILGenerator();
getterILCode.Emit(OpCodes.Ldarg_0);
getterILCode.Emit(OpCodes.Ldfld, fieldBuilder);
getterILCode.Emit(OpCodes.Ret);
// Define the setter method.
MethodBuilder setterMethod = builder.DefineMethod(
string.Concat(SetterPrefix, propertyName),
propertyMethodAttributes,
null,
new Type[] { propertyType });
// Emit the IL code.
// ldarg.0
// ldarg.1
// stfld,_field
// ret
ILGenerator setterILCode = setterMethod.GetILGenerator();
setterILCode.Emit(OpCodes.Ldarg_0);
setterILCode.Emit(OpCodes.Ldarg_1);
setterILCode.Emit(OpCodes.Stfld, fieldBuilder);
setterILCode.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterMethod);
propertyBuilder.SetSetMethod(setterMethod);
}
}
四.ef4.1 如何執行SQL函數等操作?
添加引用 System.Data.Objects.SqlClient.SqlFunctions 主要是這個命名空間
使用方法~上一個工作中的例子~
var query = from s in student.T_StudentInfo
where SqlFunctions.DateDiff("day", s.CreateTime, "2011/11/4") == 0
select s.StudentName;
使用SQL 的datadiff 函數~~
五.ef4.1 如何跨數據庫訪問?
每次別人問我這個問題 毫不猶豫的把站長dudu的文章發過去~ 他已經很好的解決了~
http://www.cnblogs.com/dudu/archive/2011/03/29/entity_framework_cross_database_query_fact.html
核心思路 欺騙SQL 利用創建同義詞去實現
六.ef4.1執行連接查詢?什么時候執行左連接? 什么時候執行內連接? ef 根據什么去判斷?
當我們做多表查詢時 用Include 強制加載 或用 select 去查詢時 發現生成的SQL語句 有時是左連接 有時是inner join。
其實EF是根據我們實體類的連接字段 是否可空來判斷的~比如外鍵 studentID
public Nullable<int> StudentID { get; set; }
是否可空 就會造成 是 left join 還是 inner join~~
補充下~~ 有個朋友說 這個設為空了 依然執行的是內連接啊~
注意看下你的關系那塊 也要設為可空 用這個 HasOptional 而不要用 HasRequired ~~
當你的外鍵可以為空時 用 HasOptional 否則用 HasRequired
這塊也會決定你是內鏈接 還是 左連接~~
七.新手使用ef4.1 常見的一些報錯信息
1.執行命令定義時出錯
出現這個錯的原因有很多 數據庫語句錯誤 我們可以先通過監測SQL 語句是否發送到數據庫 然后執行這條SQL語句 看看是否有問題
造成這個錯的原因 還有可能是 連接對象一直被占用 因為EF有延遲加載 只是select時 並沒有真正去數據庫執行
我們可以先把前面的查詢語句 tolist等 再去執行下面的操作
2.
System.Data.Edm.EdmEntityType: : EntityType“Enrollment”未定義鍵。請為該 EntityType 定義鍵。
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �Enrollments� 基於未定義鍵的類型 �Enrollment�。
遇到這種情況 嘗試給主鍵加上[Key]
3.更新條目錯誤
依然檢測數據庫語句 是否有外鍵約束導致插入錯誤等
4.LINQ to Entities 不識別方法“System.String ToString(System.String)”因此該方法無法轉換為存儲表達式
或者不識別其他方法......類似於這樣的錯誤
因為SQL里沒有這樣的方法 所以無法轉換成SQL語句 SqlClient 和Linq Provider沒有實現那個方法對應的SQL,所以會提示不支持
解決辦法:
1. 把要轉換的值提前轉換好 而不要再 linq 或拉姆達表示里寫 這樣的轉換語。
就是把變量 .ToString() 提到外面聲明個變量 然后在拉姆達表達式里 直接使用這個變量
2. 轉換成 Enumerable
IEnumerable是直接執行方法 ,而不調用Provider來轉成其它的方式
這樣會把數據庫里的查詢出來 然后在內存里操作 所以數據庫量大時 效率會低~
八.ef4.1使用datatable
datatable 在有的時候是非常有用的 例如 做報表等 因為我們不可能為每個報表建一個 實體類 這樣比較麻煩
這個時候返回datatable 則比較有用
寫一個擴展方法
/// <summary> /// EF SQL 語句返回 dataTable /// </summary> /// <param name="db"></param> /// <param name="sql"></param> /// <param name="parameters"></param> /// <returns></returns> public static DataTable SqlQueryForDataTatable(this Database db, string sql, SqlParameter[] parameters) { SqlConnection conn = new System.Data.SqlClient.SqlConnection(); conn.ConnectionString = db.Connection.ConnectionString; if (conn.State != ConnectionState.Open) { conn.Open(); } SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.CommandText = sql; if (parameters.Length>0) { foreach (var item in parameters) { cmd.Parameters.Add(item); } } SqlDataAdapter adapter = new SqlDataAdapter(cmd); DataTable table = new DataTable(); adapter.Fill(table); return table; }
調用如下
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { GridView1.DataSource = GetDataTable(); GridView1.DataBind(); } } public DataTable GetDataTable() { GardenHotelContext context = new GardenHotelContext(); int LanType = 0; int state = 0; SqlParameter[] sqlparams=new SqlParameter[2]; sqlparams[0]=new SqlParameter("LanType",LanType); sqlparams[1]=new SqlParameter("state",state); DataTable DataTable = context.Database.SqlQueryForDataTatable("select LeaveName,LeaveEmail from LeaveInfo where LanType=@LanType and State=@State", sqlparams); return DataTable; }
再分享一種方法 先上調用效果 利用返回的var 匿名類型 這樣就無需聲明實體類了
public DataTable GetDataTable2() { GardenHotelContext context = new GardenHotelContext(); var list = (from l in context.LeaveInfoes group l by l.LanType into g select new { g.Key, num = g.Count() }).ToList(); return PubClass.ListToDataTable(list); }
核心方法 反射調用
#region 反射List To DataTable /// <summary> /// 將集合類轉換成DataTable /// </summary> /// <param name="list">集合</param> /// <returns></returns> public static DataTable ListToDataTable(IList list) { DataTable result = new DataTable(); if (list.Count > 0) { PropertyInfo[] propertys = list[0].GetType().GetProperties(); foreach (PropertyInfo pi in propertys) { result.Columns.Add(pi.Name, pi.PropertyType); } for (int i = 0; i < list.Count; i++) { ArrayList tempList = new ArrayList(); foreach (PropertyInfo pi in propertys) { object obj = pi.GetValue(list[i], null); tempList.Add(obj); } object[] array = tempList.ToArray(); result.LoadDataRow(array, true); } } return result; } #endregion
當然 解決這個問題 也可以用 上面的 動態視圖的方法去解決~
九.總結
目前上面這幾個問題 被問的比較多~ 寫個文章 以后就不用再回答類似的問題啦~
我的解決方法不是最好的 有更好的解決辦法 歡迎回復~期待你的精彩回復!
如果大家還遇到什么EF4.1的問題 或者 mvc3上的問題 都歡迎留言 ~我盡力幫大家
解決~