1. 程式人生 > >MVC3+EF4.1學習系列(十一)----EF4.1常見的問題解決

MVC3+EF4.1學習系列(十一)----EF4.1常見的問題解決

部落格寫了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 韋 給的幫助~~

SqlQueryForDynamic的擴充套件方法
  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的文章發過去~ 他已經很好的解決了~

核心思路 欺騙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 [email protected] and [email protected]", 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上的問題 都歡迎留言 ~我盡力幫大家

解決~

相關推薦

MVC3+EF4.1學習系列()----EF4.1常見的問題解決

部落格寫了10篇了~有很多朋友私信問了一些問題,而且很多問題 大家問的都一樣 這裡說說這些常見問題的解決辦法.如果大家有更好的解決辦法~也希望分享出來 問題大概為這幾個 一.ef4.1 codeFirst 修改表結構 增加欄位等 EF code first需要重新生成庫導致

Spark-SparkSQL深入學習系列(轉自OopsOutOfMemory)

  上週Spark1.2剛釋出,週末在家沒事,把這個特性給瞭解一下,順便分析下原始碼,看一看這個特性是如何設計及實現的。     /** Spark SQL原始碼分析系列文章*/ 一、Sources包核心     Spark SQL在Spark1.2中提供了Exte

學習ASP.NET Core Razor 編程系列——把新字段更新到數據庫

data 工具 itl tle 16px sed 目錄 mep 分享圖片 學習ASP.NET Core Razor 編程系列目錄 學習ASP.NET Core Razor 編程系列一 學習ASP.NET Core Razor 編程系列二——添加一

機器學習與深度學習系列連載: 第二部分 深度學習)卷積神經網路 2 Why CNN for Image?

卷積神經網路 2 Why CNN 為什麼處理圖片要用CNN? 原因是: 一個神經元無法看到整張圖片 能夠聯絡到小的區域,並且引數更少 圖片壓縮畫素不改變圖片內容 1. CNN 的特點 卷積: 一些卷積核遠遠小於圖片大小; 同樣的pat

機器學習與深度學習系列連載: 第一部分 機器學習)決策樹2(Decision Tree)

決策樹2 決策樹很容易出現過擬合問題,針對過擬合問題,我們採用以下幾種方法 劃分選擇 vs 剪枝 剪枝 (pruning) 是決策樹對付“過擬合”的 主要手段! 基本策略: 預剪枝 (pre-pruning): 提前終止某些分支的生長 後剪枝 (post-pr

Linux基礎學習系列

內核版本 比較 其中 問題 測試版 工具 含義 語言 復制   Linux是一種類似於UNIX的操作系統,由Linus Torvalds於1991年在minix操作系統的基礎創建。Linux憑借其優良特性已經成為目前發展潛力最大的操作系統。   Linux的版本有內核版本和

spring boot學習系列

web服務器 應用程序 spring 控制器 做什麽 spring boot開發第一個應用程序1、spring boot是什麽?2、spring boot容易上手嗎?寫這篇文章技術文章,主要是記錄日常的學習以及理解。我們重新認識一下spring假設你受命使用spring開發一個簡單的hel

ES6-----學習系列(set-map數據結構)

trie for strong 沒有 類型轉化 數組去重 foreach 生效 nbsp 一、set數據結構 (使用 new來創建一個set集合 通過add方法添加元素 通過size來獲取set集合的長度)  { let list = new Set();

ES6-----學習系列七(Decorator)

col 修飾符 script hello fun only 概念 new ret Decorator就是修飾器的意思 一、概念   修飾器是一個函數,用來修改類的行為(註意:1、函數 2、修改行為 3、對類進行操作)   1、只讀的修飾器 { let reado

python學習) 文件和流

內存 無緩沖 abcd 連接 sss 文件操作 打開文件 while 3.5 11.1 打開文件   >>> f = open(r‘c:\text\somefile.txt‘), 第一個參數是文件名,必須有;第二個是模式;第三個參數是緩沖。   11.1

優秀開源軟件學習系列)——從零學習Spring4以及學習方法分享

文檔 軟件 準備 相關性 培訓 獎勵 在哪裏 方式 列表 一、目的1.掌握Spring4怎樣使用,以便將這個框架作為自己的一項技能。2.掌握Spring官網是怎樣介紹其產品的,在心中對Spring有最官方的、最直觀的了解。在Spring的相關領域,能夠知道怎麽下載Sprin

RabbitMQ學習系列): 介紹

ref 原理 二維碼 host 屬性 訂閱 什麽 設計 發的 1. 介紹   RabbitMQ是一個由erlang開發的基於AMQP(Advanced Message Queue )協議的開源實現。用於在分布式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面都非

java核心學習(二) 多線程---創建啟動線程的三種方式

使用 trace 啟動 操作系統 java多線程 start name image 獲取    本節開始java多線程編程的學習,對於操作系統、進程、線程的基本概念不再贅述,只是了解java對於多線程編程的支持有哪些。 一、繼承Thread類來創建線程    java語言中

WPF入門教程系列——依賴屬性(

nts 如果 edev 出現 樣式 語法 寫法 屬性。 結構 一、依賴屬性基本介紹   本篇開始學習WPF的另一個重要內容依賴屬性。 大家都知道WPF帶來了很多新的特性,其中一個就是引入了一種新的屬性機制——依賴屬性。依賴屬性出現的目的是用來實現WPF中的樣式、自

linux學習)用戶和用戶組管理

gin rec uda username type /usr 之前 密碼 只有一個 一、用戶文件 文件:/etc/passwd 這個文件記錄了用戶了用戶名,用戶id,所屬組,家目錄,shell信息: [root@iZ25lzba47vZ ~]# tail -n3 /etc

mybatis學習)——springmvc++spring+mybatis整合

transacti servlet 自動註入 為我 reac content attribute 定義 property 做任何一個項目都以一個需求,這裏先定義一下需求:利用三大框架查詢酒店列表。 一、搭建開發環境 1、創建一個web項目 我這裏用的是 jdk1.8+to

Nginx學習-Nginx啟動框架處理流程

table ssl 優先級 init int 數組 linux cmd 默認 Nginx啟動過程流程圖 下面首先給出Nginx啟動過程的流程圖: ngx_cycle_t結構體 Nginx的啟動初始化在src/core/nginx.c的main函數中完成,當然main

linux系統學習天-<<工程師技術>>

linux工程師技術 linux管理員技術 linux雲計算工程師 深圳雲計算王森 雲計算運維工程師 兩臺虛擬機,均要檢測1. Yum是否可用 2. 防火墻默認區域修改為trusted 3. IP地址是否配置########################################

Docker學習系列):windows下安裝docker

阻止 statistic pro nta 雙擊 copyright ner notebook 現在 本文目錄如下: windows按照docker的基本要求 具體安裝步驟 開始使用 安裝遠程連接工具連接docker 安裝中遇到的問題 Docker的更新 Dock

javaweb學習總結()——使用Cookie進行會話管理(轉)

緩存 利用 iter() 自然 web har oca main end 一、會話的概念   會話可簡單理解為:用戶開一個瀏覽器,點擊多個超鏈接,訪問服務器多個web資源,然後關閉瀏覽器,整個過程稱之為一個會話。  有狀態會話:一個同學來過教室,下次再來教室,我們會知道這個