2012-01-31 71 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