MVC3+EF4.1學(xué)習(xí)系列(十一)----EF4.1常見(jiàn)的問(wèn)題解決
博客寫(xiě)了10篇了~有很多朋友私信問(wèn)了一些問(wèn)題,而且很多問(wèn)題 大家問(wèn)的都一樣 這里說(shuō)說(shuō)這些常見(jiàn)問(wèn)題的解決辦法.如果大家有更好的解決辦法~也希望分享出來(lái)
問(wèn)題大概為這幾個(gè)
一.ef4.1 codeFirst 修改表結(jié)構(gòu) 增加字段等 EF code first需要重新生成庫(kù)導(dǎo)致數(shù)據(jù)丟失的問(wèn)題.
二.ef4.1 沒(méi)有了edmx等復(fù)雜的東西 變得簡(jiǎn)單 干凈 但如何使用存儲(chǔ)過(guò)程,存儲(chǔ)過(guò)程可以返回表 可以返回?cái)?shù)值 也有可能是執(zhí)行修改 刪除 增加等 該怎么做?
三.ef4.1 如何使用數(shù)據(jù)庫(kù)視圖?每個(gè)視圖都要去建立對(duì)應(yīng)的實(shí)體類(lèi)么?有簡(jiǎn)單的方法么?
四.ef4.1 如何執(zhí)行SQL函數(shù)等操作?
五.ef4.1 如何跨數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)?
六.ef4.1執(zhí)行連接查詢(xún)?什么時(shí)候執(zhí)行左連接? 什么時(shí)候執(zhí)行內(nèi)連接? ef 根據(jù)什么去判斷?
七.新手使用ef4.1 常見(jiàn)的一些報(bào)錯(cuò)信息
八.ef4.1返回datatable 方便做報(bào)表等一些復(fù)雜操作
其實(shí)這些問(wèn)題是比較簡(jiǎn)單的~ 所以此文送給剛用ef4.1的新手~
下面開(kāi)始一一解決這些問(wèn)題
一.ef4.1 codeFirst 修改表結(jié)構(gòu) 增加字段等 EF code first需要重新生成庫(kù)導(dǎo)致數(shù)據(jù)丟失的問(wèn)題
說(shuō)這個(gè)問(wèn)題前 首先先說(shuō)下 我使用ef4.1 codefirst的目的. 是因?yàn)榭梢杂懈儍舻腜OCO 不再有EDMX這些東西 而不是真正的用 code first 先有代碼 再生成數(shù)據(jù)庫(kù).所以 我雖然使用
的是codefirst 但是本質(zhì)依然是數(shù)據(jù)庫(kù)優(yōu)先.
所以這個(gè)被問(wèn)的很多的問(wèn)題 解決辦法其實(shí)是非常簡(jiǎn)單的.只要你的數(shù)據(jù)庫(kù)已經(jīng)存在了 那么即使你用code first ef 也不會(huì)給你去生成數(shù)據(jù)庫(kù)的. 這個(gè)時(shí)候 你增加表字段 甚至增加表 只要把
實(shí)體類(lèi)也相應(yīng)的修改 則數(shù)據(jù)庫(kù)里的數(shù)據(jù) 是不會(huì)被清空的.
說(shuō)下我的開(kāi)發(fā)步驟 先設(shè)計(jì)數(shù)據(jù)庫(kù) 并建立數(shù)據(jù)庫(kù)=>通過(guò)EF工具生成映射和實(shí)體類(lèi)=>開(kāi)發(fā)代碼 當(dāng)遇到修改時(shí)=> 先修改數(shù)據(jù)庫(kù)如添加字段或表等=>再修改實(shí)體類(lèi)=>繼續(xù)開(kāi)發(fā)
這樣就不會(huì)有重新生成數(shù)據(jù)的煩惱了 而且項(xiàng)目里也不會(huì)出現(xiàn)edmx~
還有 使用EF4.3 的數(shù)據(jù)遷移功能 也可以完美解決~
二.ef4.1 沒(méi)有了edmx等復(fù)雜的東西 變得簡(jiǎn)單 干凈 但如何使用存儲(chǔ)過(guò)程,存儲(chǔ)過(guò)程可以返回表 可以返回?cái)?shù)值 也有可能是執(zhí)行修改 刪除 增加等 該怎么做?
說(shuō)這個(gè)問(wèn)題前 依然先說(shuō)下我的觀點(diǎn).個(gè)人認(rèn)為 既然使用orm框架 就應(yīng)該把業(yè)務(wù)邏輯等 都放到業(yè)務(wù)邏輯層 而不應(yīng)該再使用存儲(chǔ)過(guò)程。我更偏重重業(yè)務(wù)邏輯層 輕存儲(chǔ)過(guò)程這樣的開(kāi)發(fā)~
再ef4.0里 添加存儲(chǔ)過(guò)程 比較容易 有edmx 調(diào)一調(diào) 存儲(chǔ)過(guò)程就添加上了 但是在ef4.1里 只有干凈的poco 不再有edmx了 改怎么辦呢?尤其是存儲(chǔ)過(guò)程可以是查表 查值 或者執(zhí)行修改刪除.
一個(gè)一個(gè)來(lái)解決
1.執(zhí)行返回表類(lèi)型的存儲(chǔ)過(guò)程
先上存儲(chǔ)過(guò)程 隨手寫(xiě)的一個(gè)最簡(jiǎn)單的
Create PROCEDURE [dbo].[ProSelectStu]
@StudentID int
AS
BEGIN
Select Student.* from Enrollment,Student
where Enrollment.StudentID=Student.StudentID
and Enrollment.StudentID=@StudentID
END
GO
執(zhí)行存儲(chǔ)過(guò)程的方法 是用直接執(zhí)行sql的方式 我在我的文章第九篇 有過(guò)詳細(xì)的介紹~大家可以先去看下

執(zhí)行表的存儲(chǔ)過(guò)程 其實(shí)是非常強(qiáng)大的 延遲加載 等都有體現(xiàn) 博客園的陸老師已經(jīng)寫(xiě)了 寫(xiě)的非常清楚了~我這里就不再寫(xiě)了 大家可以去他那看下 提供個(gè)連接~
2.執(zhí)行返回值的存儲(chǔ)過(guò)程
先上存儲(chǔ)過(guò)程
CREATE PROCEDURE [dbo].[ProSelectCount]
@StuId int
AS
BEGIN
select COUNT(*) from Enrollment where StudentID=@StuId
END
一個(gè)簡(jiǎn)單的查詢(xún)數(shù)量
這里用sqlQuery 執(zhí)行訪(fǎng)問(wèn) 數(shù)據(jù)庫(kù) 因?yàn)樾枰峁┓祷仡?lèi)型 而我們返回的是int 所以先得到int的類(lèi)型

3.執(zhí)行增刪改
CREATE PROCEDURE [dbo].[ProDel]
@stuId int,
@courseId int
AS
BEGIN
DELETE FROM [WLFSchool].[dbo].[Enrollment]
where StudentID=@stuId and CourseID=@courseId
END
這個(gè)用的是操作數(shù)據(jù)庫(kù) 返回受影響行數(shù)

三.ef4.1 如何使用數(shù)據(jù)庫(kù)視圖?每個(gè)視圖都要去建立對(duì)應(yīng)的實(shí)體類(lèi)么?有簡(jiǎn)單的方法么?
先說(shuō)下最傳統(tǒng)的方法 只需把視圖 當(dāng)成表 建立對(duì)應(yīng)的實(shí)體類(lèi) 然后加到dbcontext 里即可。沒(méi)什么難度。
再說(shuō)一個(gè)問(wèn)題 使用linq 有個(gè)非常美妙的功能 投影映射 和C#3.0的 匿名函數(shù) 讓我們很多情況 不需要視圖的
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
};
再通過(guò) var result 接受上面的值 這樣我們就不用去數(shù)據(jù)庫(kù)建視圖 不用再建實(shí)體類(lèi) 是不是很省事呢?
如果公司強(qiáng)大的DBA 已經(jīng)給我們建好了很多視圖 是不是就要一個(gè)個(gè)去寫(xiě)實(shí)體類(lèi)呢?如果你使用的是C#4.0 那么可以用動(dòng)態(tài)的 來(lái)解決這個(gè)問(wèn)題~
像下面這樣使用 是不是很爽

這個(gè)不僅可以查詢(xún)視圖 普通的表 只要是SQL語(yǔ)句 都可以自動(dòng)生成動(dòng)態(tài)類(lèi) 讓你用~
下面是擴(kuò)展方法 和 使用Emit 來(lái)動(dòng)態(tài)構(gòu)建 感謝 ASP.NET 韋 給的幫助~~
SqlQueryForDynamic的擴(kuò)展方法
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; //無(wú)結(jié)果返回Null
}
#region 構(gòu)建動(dòng)態(tài)字段
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 如何執(zhí)行SQL函數(shù)等操作?
添加引用 System.Data.Objects.SqlClient.SqlFunctions 主要是這個(gè)命名空間
使用方法~上一個(gè)工作中的例子~
var query = from s in student.T_StudentInfo
where SqlFunctions.DateDiff("day", s.CreateTime, "2011/11/4") == 0
select s.StudentName;
使用SQL 的datadiff 函數(shù)~~
五.ef4.1 如何跨數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)?
每次別人問(wèn)我這個(gè)問(wèn)題 毫不猶豫的把站長(zhǎng)dudu的文章發(fā)過(guò)去~ 他已經(jīng)很好的解決了~
http://www.rzrgm.cn/dudu/archive/2011/03/29/entity_framework_cross_database_query_fact.html
核心思路 欺騙SQL 利用創(chuàng)建同義詞去實(shí)現(xiàn)
六.ef4.1執(zhí)行連接查詢(xún)?什么時(shí)候執(zhí)行左連接? 什么時(shí)候執(zhí)行內(nèi)連接? ef 根據(jù)什么去判斷?
當(dāng)我們做多表查詢(xún)時(shí) 用Include 強(qiáng)制加載 或用 select 去查詢(xún)時(shí) 發(fā)現(xiàn)生成的SQL語(yǔ)句 有時(shí)是左連接 有時(shí)是inner join。
其實(shí)EF是根據(jù)我們實(shí)體類(lèi)的連接字段 是否可空來(lái)判斷的~比如外鍵 studentID
public Nullable<int> StudentID { get; set; }
是否可空 就會(huì)造成 是 left join 還是 inner join~~
補(bǔ)充下~~ 有個(gè)朋友說(shuō) 這個(gè)設(shè)為空了 依然執(zhí)行的是內(nèi)連接啊~
注意看下你的關(guān)系那塊 也要設(shè)為可空 用這個(gè) HasOptional 而不要用 HasRequired ~~
當(dāng)你的外鍵可以為空時(shí) 用 HasOptional 否則用 HasRequired
這塊也會(huì)決定你是內(nèi)鏈接 還是 左連接~~
七.新手使用ef4.1 常見(jiàn)的一些報(bào)錯(cuò)信息
1.執(zhí)行命令定義時(shí)出錯(cuò)
出現(xiàn)這個(gè)錯(cuò)的原因有很多 數(shù)據(jù)庫(kù)語(yǔ)句錯(cuò)誤 我們可以先通過(guò)監(jiān)測(cè)SQL 語(yǔ)句是否發(fā)送到數(shù)據(jù)庫(kù) 然后執(zhí)行這條SQL語(yǔ)句 看看是否有問(wèn)題
造成這個(gè)錯(cuò)的原因 還有可能是 連接對(duì)象一直被占用 因?yàn)镋F有延遲加載 只是select時(shí) 并沒(méi)有真正去數(shù)據(jù)庫(kù)執(zhí)行
我們可以先把前面的查詢(xún)語(yǔ)句 tolist等 再去執(zhí)行下面的操作
2.
System.Data.Edm.EdmEntityType: : EntityType“Enrollment”未定義鍵。請(qǐng)為該 EntityType 定義鍵。
System.Data.Edm.EdmEntitySet: EntityType: EntitySet ?Enrollments? 基于未定義鍵的類(lèi)型 ?Enrollment?。
遇到這種情況 嘗試給主鍵加上[Key]
3.更新條目錯(cuò)誤
依然檢測(cè)數(shù)據(jù)庫(kù)語(yǔ)句 是否有外鍵約束導(dǎo)致插入錯(cuò)誤等
4.LINQ to Entities 不識(shí)別方法“System.String ToString(System.String)”因此該方法無(wú)法轉(zhuǎn)換為存儲(chǔ)表達(dá)式
或者不識(shí)別其他方法......類(lèi)似于這樣的錯(cuò)誤
因?yàn)镾QL里沒(méi)有這樣的方法 所以無(wú)法轉(zhuǎn)換成SQL語(yǔ)句 SqlClient 和Linq Provider沒(méi)有實(shí)現(xiàn)那個(gè)方法對(duì)應(yīng)的SQL,所以會(huì)提示不支持
解決辦法:
1. 把要轉(zhuǎn)換的值提前轉(zhuǎn)換好 而不要再 linq 或拉姆達(dá)表示里寫(xiě) 這樣的轉(zhuǎn)換語(yǔ)。
就是把變量 .ToString() 提到外面聲明個(gè)變量 然后在拉姆達(dá)表達(dá)式里 直接使用這個(gè)變量
2. 轉(zhuǎn)換成 Enumerable
IEnumerable是直接執(zhí)行方法 ,而不調(diào)用Provider來(lái)轉(zhuǎn)成其它的方式
這樣會(huì)把數(shù)據(jù)庫(kù)里的查詢(xún)出來(lái) 然后在內(nèi)存里操作 所以數(shù)據(jù)庫(kù)量大時(shí) 效率會(huì)低~
八.ef4.1使用datatable
datatable 在有的時(shí)候是非常有用的 例如 做報(bào)表等 因?yàn)槲覀儾豢赡転槊總€(gè)報(bào)表建一個(gè) 實(shí)體類(lèi) 這樣比較麻煩
這個(gè)時(shí)候返回datatable 則比較有用
寫(xiě)一個(gè)擴(kuò)展方法
/// <summary> /// EF SQL 語(yǔ)句返回 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; }
調(diào)用如下
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; }
再分享一種方法 先上調(diào)用效果 利用返回的var 匿名類(lèi)型 這樣就無(wú)需聲明實(shí)體類(lèi)了
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); }
核心方法 反射調(diào)用
#region 反射List To DataTable /// <summary> /// 將集合類(lèi)轉(zhuǎn)換成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
當(dāng)然 解決這個(gè)問(wèn)題 也可以用 上面的 動(dòng)態(tài)視圖的方法去解決~
九.總結(jié)
目前上面這幾個(gè)問(wèn)題 被問(wèn)的比較多~ 寫(xiě)個(gè)文章 以后就不用再回答類(lèi)似的問(wèn)題啦~
我的解決方法不是最好的 有更好的解決辦法 歡迎回復(fù)~期待你的精彩回復(fù)!
如果大家還遇到什么EF4.1的問(wèn)題 或者 mvc3上的問(wèn)題 都?xì)g迎留言 ~我盡力幫大家
解決~

浙公網(wǎng)安備 33010602011771號(hào)