Entity Framework 4.1常見的問題解決

作者: wlf  來源: 博客園  發布時間: 2011-12-30 17:03  閱讀: 20680 次  推薦: 13   原文鏈接   [收藏]  

  博客寫了10篇了~有很多朋友私信問了一些問題,而且很多問題 大家問的都一樣 這里說說這些常見問題的解決辦法.如果大家有更好的解決辦法~也希望分享出來

  問題大概為這幾個

  一.ef4.1 codeFirst 修改表結構 增加字段等 EF code first需要重新生成庫導致數據丟失的問題.

  二.ef4.1 沒有了edmx等復雜的東西 變得簡單 干凈  但如何使用存儲過程,存儲過程可以返回表 可以返回數值 也有可能是執行修改 刪除 增加等  該怎么做?

  三.ef4.1 如何使用數據庫視圖?每個視圖都要去建立對應的實體類么?有簡單的方法么?

  四.ef4.1 如何執行SQL函數等操作?

  五.ef4.1 如何跨數據庫訪問?

  六.ef4.1執行連接查詢?什么時候執行左連接? 什么時候執行內連接? ef 根據什么去判斷?

  七.新手使用ef4.1 常見的一些報錯信息

  其實這些問題是比較簡單的~ 所以此文送給剛用ef4.1的新手~

  下面開始一一解決這些問題

  一.ef4.1 codeFirst 修改表結構 增加字段等 EF code first需要重新生成庫導致數據丟失的問題

  說這個問題前 首先先說下 我使用ef4.1 codefirst的目的. 是因為可以有更純凈的POCO 不再有EDMX這些東西  而不是真正的用 code first 先有代碼 再生成數據庫.所以 我雖然使用

  的是codefirst 但是本質依然是數據庫優先.

  所以這個被問的很多的問題 解決辦法其實是非常簡單的.只要你的數據庫已經存在了 那么即使你用code first ef 也不會給你去生成數據庫的. 這個時候 你增加表字段 甚至增加表 只要把

  實體類也相應的修改 則數據庫里的數據 是不會被清空的.

  說下我的開發步驟  先設計數據庫 并建立數據庫=>通過EF工具生成映射和實體類=>開發代碼     當遇到修改時=>  先修改數據庫如添加字段或表等=>再修改實體類=>繼續開發

  這樣就不會有重新生成數據的煩惱了 而且項目里也不會出現edmx~

  二.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的方式 我在我的文章第九篇 有過詳細的介紹~大家可以先去看下

  執行表的存儲過程 其實是非常強大的 延遲加載 等都有體現 博客園的陸老師已經寫了 寫的非常清楚了~我這里就不再寫了 大家可以去他那看下 提供個連接~

  EF使用存儲過程查詢表的

  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~~
  七.新手使用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.更新條目錯誤

  依然檢測數據庫語句 是否有外鍵約束導致插入錯誤等

  八.總結

  目前上面這幾個問題 被問的比較多~ 寫個文章 以后就不用再回答類似的問題啦~

  我的解決方法不是最好的  有更好的解決辦法 歡迎回復~期待你的精彩回復!

  如果大家還遇到什么EF4.1的問題 或者 mvc3上的問題 都歡迎留言 ~我盡力幫大家

  解決~

 
 

文章列表

arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()