2012-01-31 68 views
3

我正在寫一個代碼生成和我被陷在確定一個存儲過程的結果集列的可空狀態的存儲過程結果的列可以爲空。我可以查詢的數據類型就好了,但無論是DataReader對象,也不是數據表列包含我的專欄的正確空值。我怎樣才能確定是否

 public List<DataColumn> GetColumnInfoFromStoredProcResult(string schema, string storedProcName) 
    { 
     //build sql text 
     var sb = new StringBuilder(); 
     sb.Append("SET FMTONLY OFF; SET FMTONLY ON; \n");//this is how EF4.1 did so I copied..not sure why the repeat 

     sb.Append(String.Format("exec {0}.{1} ", schema, storedProcName)); 

     var prms = GetStoredProcedureParameters(schema: schema, sprocName: storedProcName); 
     var count = 1; 
     foreach (var param in prms) 
     { 
      sb.Append(String.Format("{0}=null", param.Name)); 
      if (count < prms.Count) 
      { 
       sb.Append(", "); 
      } 
      count++; 
     } 

     sb.Append("\n SET FMTONLY OFF; SET FMTONLY OFF;"); 

     var dataTable = new DataTable(); 
     //var list = new List<DataColumn>(); 

     using (var sqlConnection = this.SqlConnection) 
     { 
      using (var sqlAdapter = new SqlDataAdapter(sb.ToString(), sqlConnection)) 
      { 
       if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open(); 
       sqlAdapter.SelectCommand.ExecuteReader(CommandBehavior.KeyInfo); 
       sqlConnection.Close(); 
       sqlAdapter.Fill(dataTable); 
      } 

      //using (var sqlCommand = new SqlCommand()) 
      //{ 

      // sqlCommand.CommandText = sb.ToString(); 
      // sqlCommand.CommandType = CommandType.Text; 
      // sqlCommand.Connection = sqlConnection; 
      // if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open(); 

      // var dr = sqlCommand.ExecuteReader(CommandBehavior.SchemaOnly); 
      // var whateva = dr.GetSchemaTable(); 

      // foreach (DataColumn col in whateva.Columns) 
      // { 
      //  list.Add(col); 
      // } 
      //} 
     } 

     var list = dataTable.Columns.Cast<DataColumn>().ToList(); 

     return list; 
    } 

我試圖從一個存儲過程類似於在實體框架創建一個複雜類型的東西結束。我可以劫持該功能嗎?

在這個例子中,ID列.. tblJobId(不是我個人的稱呼)將不能爲空。但我選擇null作爲ImNull,它具有所有的相同屬性那麼,如何EF確定相應的C#數據類型應可以爲空還是不可以?

有沒有人做過這個..

想法是讚賞。

enter image description here

+0

*我試圖從一個存儲過程類似於在實體框架創建一個複雜類型的東西結束。我可以劫持該功能嗎? - 是的,使用實體框架。 – 2012-01-31 05:25:13

+0

此代碼生成器爲企業數據訪問塊創建存儲過程調用。我使用Linq到EDMX來爲實體生成中間層和自動導入sprocs。好東西,但我只是想確定列是否爲空或不那麼我可以創建存儲過程的結果屬性和對象的一部分。 EF竟然是我的表計太慢了,我厭倦了等待的「嚮導」,甚至更新整個EDMX時,我只更新一個存儲過程。 – TheDev6 2012-01-31 05:34:03

+0

您可以依靠標準的SMO呼叫。我用'StoredProcedureParameter.DefaultValue'參數取得了一些成功。 – 2012-01-31 05:41:05

回答

3

祕密是使用Schema Only並填充數據集不可數據表。現在,datacolumn上的AllowDbNull屬性正確顯示返回值的可空狀態。

這是它...

public List<DataColumn> GetColumnInfoFromStoredProcResult(string schema, string storedProcName) 
    { 
     //build sql text 
     var sb = new StringBuilder(); 
     sb.Append("SET FMTONLY OFF; SET FMTONLY ON; \n");//this is how EF4.1 did so I copied..not sure why the repeat 

     sb.Append(String.Format("exec {0}.{1} ", schema, storedProcName)); 

     var prms = GetStoredProcedureParameters(schema: schema, sprocName: storedProcName); 
     var count = 1; 
     foreach (var param in prms) 
     { 
      sb.Append(String.Format("{0}=null", param.Name)); 
      if (count < prms.Count) 
      { 
       sb.Append(", "); 
      } 
      count++; 
     } 

     sb.Append("\n SET FMTONLY OFF; SET FMTONLY OFF;"); 

     var ds = new DataSet(); 
     using (var sqlConnection = this.SqlConnection) 
     { 
      using (var sqlAdapter = new SqlDataAdapter(sb.ToString(), sqlConnection)) 
      { 
       if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open(); 
       sqlAdapter.SelectCommand.ExecuteReader(CommandBehavior.SchemaOnly); 
       sqlConnection.Close(); 
       sqlAdapter.FillSchema(ds, SchemaType.Source, "MyTable"); 
      } 
     } 

     var list = ds.Tables[0].Columns.Cast<DataColumn>().ToList(); 

     return list; 
    } 

    public List<SqlParamInfo> GetStoredProcedureParameters(string schema, string sprocName) 
    { 
     var sqlText = String.Format(
      @"SELECT 
       [Name] = N'@RETURN_VALUE', 
       [ID] = 0, 
       [Direction] = 6, 
       [UserType] = NULL, 
       [SystemType] = N'int', 
       [Size] = 4, 
       [Precision] = 10, 
       [Scale] = 0 
      WHERE 
       OBJECTPROPERTY(OBJECT_ID(N'{0}.{1}'), 'IsProcedure') = 1 
      UNION 
      SELECT 
       [Name] = CASE WHEN p.name <> '' THEN p.name ELSE '@RETURN_VALUE' END, 
       [ID] = p.parameter_id, 
       [Direction] = CASE WHEN p.is_output = 0 THEN 1 WHEN p.parameter_id > 0 AND p.is_output = 1 THEN 3 ELSE 6 END, 
       [UserType] = CASE WHEN ut.is_assembly_type = 1 THEN SCHEMA_NAME(ut.schema_id) + '.' + ut.name ELSE NULL END, 
       [SystemType] = CASE WHEN ut.is_assembly_type = 0 AND ut.user_type_id = ut.system_type_id THEN ut.name WHEN ut.is_user_defined = 1 OR ut.is_assembly_type = 0 THEN st.name WHEN ut.is_table_type =1 Then 'STRUCTURED' ELSE 'UDT' END, 
       [Size] = CONVERT(int, CASE WHEN st.name IN (N'text', N'ntext', N'image') AND p.max_length = 16 THEN -1 WHEN st.name IN (N'nchar', N'nvarchar', N'sysname') AND p.max_length >= 0 THEN p.max_length/2 ELSE p.max_length END), 
       [Precision] = p.precision, 
       [Scale] = p.scale 
      FROM 
       sys.all_parameters p 
       INNER JOIN sys.types ut ON p.user_type_id = ut.user_type_id 
       LEFT OUTER JOIN sys.types st ON ut.system_type_id = st.user_type_id AND ut.system_type_id = st.system_type_id 
      WHERE 
       object_id = OBJECT_ID(N'{0}.{1}') 
      ORDER BY 2", schema, sprocName); 


     using (var sqlConnection = this.SqlConnection) 
     { 
      using (var sqlCommand = new SqlCommand()) 
      { 
       if (sqlConnection.State != ConnectionState.Open) sqlConnection.Open(); 

       sqlCommand.Connection = sqlConnection; 
       sqlCommand.CommandType = CommandType.Text; 
       sqlCommand.CommandText = sqlText; 

       var dr = sqlCommand.ExecuteReader(); 

       var result = new List<SqlParamInfo>(); 

       while (dr.Read()) 
       { 
        if (Convert.ToString(dr["Name"]) != "@RETURN_VALUE") 
        { 
         result.Add(new SqlParamInfo(dr)); 
        } 
       } 

       return result; 
      } 
     } 
    } 
+0

這正是我所需要的。我正在做一個類似的項目,這非常有幫助!謝謝! – Halcyon 2014-10-13 20:16:54

+0

這裏是一個代碼的版本的鏈接... http://enterprisecodegen.azurewebsites.net/和我已經移動到一個項目使用T4模板,因爲我的代碼gen從來沒有超過一個項目在同一時間。 www.achadwick.com。如果你想要的話,打我的源代碼。該DLL可以在這裏找到https://github.com/TheDev6/Koozy/tree/master/CodeGeneration/CodeGen。另外http://brewdawg.github.io/Tiraggo.EF/完全解壓EDMX(我認爲它將在EF7中消失?),這非常有用。 – TheDev6 2014-11-13 00:11:52

0

假設,每一個這來自SP柱可以爲空 - 這是因爲存儲過程的一個有效的假設 - 其一種數據抽象層,因此,其代碼可以改變的,但仍然產生有效結果。

如果列是不可爲空昨天沒什麼意思了今天。所以 - 來自SP結果集的所有列都可以通過設計來空。

更新。

假設表T1具有列Id INT IDENTITY PRIMARY KEY

你的存儲過程是這樣的:

CREATE PROC p1 
AS 
BEGIN 
    SELECT Id FROM t1 
END 

所以它永遠不會返回一個id = NULL,但這是SP - 數據的抽象,所以 - 明天我將修改它是這樣的:

CREATE PROC p1 
AS 
BEGIN 
    SELECT Id FROM t1 
    UNION 
    SELECT NULL 
END 

所以,現在返回NULL - 想一想。理解數據抽象的差異

+0

謝謝..但主鍵的情況下,是身份,而不是空列?我知道它是否被選爲任何查詢的一部分,它永遠不會是空的,我可以將它映射到一個不可爲空的類型。我的數據庫是規範化和相關的,如果我在不重構相關代碼的情況下進行更改,我希望它會中斷。 – TheDev6 2012-01-31 05:42:03

+0

查看附件示例 – 2012-01-31 06:01:03

+0

您瞭解該問題,但我不希望C#在我的目標是隱式定義對象時不會靜態接受來自sql proc的任何結果集。如果proc發生變化,我必須重新訪問我的C#映射到obj,沒有什麼不同,如果我選擇一個額外的列。數據抽象看起來更像是一個關於如何將sql結果連接到C#對象的體系結構決策,而不是我將C#類型結果隱式匹配到我的問題的首選目標。我想要案例1 =「int Id {get; set;}」Case2 =「int?Id {get; set;」也許我的目標更像是對象關係映射。 – TheDev6 2012-01-31 23:11:47