2012-09-26 42 views
2

我得到下面的錯誤時,允許我所說的Select功能:數據庫名稱不與表值參數

傳入的表格格式的數據流(TDS)遠程過程調用(RPC) 協議流不正確。表值參數3 (「@SearchTableVar」),第0行,第0列:數據類型0xF3(用戶定義的 表類型)具有指定的非零長度數據庫名稱。數據庫 名稱不允許使用表值參數,只有模式名稱 和類型名稱有效。

C#代碼

//DTO 
public class SP_SearchEntity_Result 
{ 
    public string ID { get; set; } 
    public string NAME { get; set; } 
} 

//Businesslogic 
public IQueryable Select(int PageIndex, int PageSize, List<KeyValuePair<string, string>> SearchBy, List<KeyValuePair<string, System.Data.SqlClient.SortOrder>> SortBy) 
{ 
    SqlDatabase obj = (SqlDatabase)DatabaseFactory.CreateDatabase();//System.Configuration.ConfigurationManager.ConnectionStrings["MySqlServer"].ConnectionString 
    return obj.ExecuteSprocAccessor<SP_SearchEntity_Result>("SP_SearchEntity", PageIndex, PageSize, SearchBy.ToDataTable(), SortBy.ToDataTable()).AsQueryable<SP_SearchEntity_Result>(); 
} 

//Extension methods 
public static DataTable ToDataTable(this List<KeyValuePair<string, string>> source) 
{ 
    DataTable dataTable = new DataTable("Test"); 
    dataTable.Columns.Add("KEY",typeof(System.String)); 
    dataTable.Columns.Add("VALUE", typeof(System.String)); 

    foreach (KeyValuePair<string, string> data in source) 
    { 
     var dr = dataTable.NewRow(); 
     dr["KEY"] = data.Key; 
     dr["VALUE"] = data.Value; 
     dataTable.Rows.Add(dr); 
    } 

    return dataTable; 
} 

public static DataTable ToDataTable(this List<KeyValuePair<string, System.Data.SqlClient.SortOrder>> source) 
{ 
    DataTable dataTable = new DataTable("Test"); 
    dataTable.Columns.Add("KEY", typeof(System.String)); 
    dataTable.Columns.Add("VALUE", typeof(System.String)); 

    foreach (KeyValuePair<string, System.Data.SqlClient.SortOrder> data in source) 
    { 
     var dr = dataTable.NewRow(); 
     dr["KEY"] = data.Key; 
     dr["VALUE"] = data.Value == System.Data.SqlClient.SortOrder.Ascending ? "ASC" : "DESC"; 
     dataTable.Rows.Add(dr); 
    } 

    return dataTable; 
} 



存儲過程返回結果的兩個表

SQL PROC定義

CREATE TYPE KeyValueTableVariable AS TABLE 
(
    [KEY] NVARCHAR(800), 
    [VALUE] NVARCHAR(800) 
) 
GO 
CREATE PROCEDURE SP_SearchEntity 
@PageIndex INT=NULL,  
@PageSize INT=NULL,  
@SearchTableVar dbo.KeyValueTableVariable READONLY, 
@SortTableVar dbo.KeyValueTableVariable READONLY 
AS 
BEGIN 
    /*Bla bla bla*/ 
    SELECT '1' as [ID], 'Nitin' as [NAME] 
    SELECT '1' as [COUNT] 
END 
+0

什麼是ToDataTable()? –

+0

它是擴展方法,問題更新 –

+0

您可能想重新考慮您的proc名稱。這使得有趣的閱讀http://dba.stackexchange.com/questions/25348/still-wrong-to-start-the-name-of-a-user-stored-procedure-with-sp – Fred

回答

1

我發現存儲過程參數的xml數據類型更容易使用。而非參數強制轉換爲數據表,你將它們轉換成XML爲下面的例子:

CREATE PROCEDURE SP_SearchEntity 
@PageIndex INT=NULL,  
@PageSize INT=NULL,  
@SearchTableVar xml=NULL, 
@SortTableVar xml=NULL 
AS 
BEGIN 
    /*Bla bla bla*/ 
    SELECT '1' as [ID], 'Nitin' as [NAME] 
    SELECT '1' as [COUNT] 
END 

這裏的KeyValuePair和查詢的樣本,它是作爲XML序列化後:

declare @sampleXml xml = ' 
<ArrayOfKeyValuePairOfstringstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic"> 
    <KeyValuePairOfstringstring> 
    <key>foo</key> 
    <value>bar</value> 
    </KeyValuePairOfstringstring> 
    <KeyValuePairOfstringstring> 
    <key>hello</key> 
    <value>world</value> 
    </KeyValuePairOfstringstring> 
</ArrayOfKeyValuePairOfstringstring>' 

select 
     Node.Elem.value('*:key[1]', 'nvarchar(800)') as [Key] 
     ,Node.Elem.value('*:value[1]', 'nvarchar(800)') as Value 
    from @sampleXml.nodes(N'/*:ArrayOfKeyValuePairOfstringstring/*:KeyValuePairOfstringstring') Node(Elem) 
go 

和XML序列:

// from Plinqo: http://www.codesmithtools.com/product/frameworks 
public static string ToXml<T>(this T item) 
{ 
    var settings = new XmlWriterSettings(); 
    settings.Indent = true; 
    settings.OmitXmlDeclaration = true; 

    var sb = new System.Text.StringBuilder(); 
    using (var writer = XmlWriter.Create(sb, settings)) 
    { 
     var serializer = new DataContractSerializer(typeof(T)); 
     serializer.WriteObject(writer, item); 
    } 

    return sb.ToString(); 
} 


編輯:返回含多處e結果集並將它們綁定到對象

我會告訴你如何做到這一點,但我不確定這是你想要做什麼,基於你的模擬SQL。如果你真的只是返回一些返回的對象,你可以在IQueryable之後計算你的結果。

首先,您需要一種綁定對象的方式,您可以通過擴展MVC來獲得這些對象。這些模型綁定器期望您的查詢返回與您的模型屬性匹配的列名稱。

using System; 
using System.Collections.Generic; 
using System.Web.Mvc; 

public partial class ModelBinder 
{ 
    /// <summary> 
    /// Binds the values of an Dictionary to a POCO model 
    /// </summary> 
    public virtual T BindModel<T>(IDictionary<string, object> dictionary) 
    { 
     DictionaryValueProvider<object> _dictionaryValueProvider = new DictionaryValueProvider<object>(dictionary, null); 
     return BindModel<T>(_dictionaryValueProvider); 
    } 

    /// <summary> 
    /// Binds the values of an IValueProvider collection to a POCO model 
    /// </summary> 
    public virtual T BindModel<T>(IValueProvider dictionary) 
    { 
     Type _modelType = typeof(T); 
     var _modelConstructor = _modelType.GetConstructor(new Type[] { }); 
     object[] _params = new object[] { }; 
     string _modelName = _modelType.Name; 
     ModelMetadata _modelMetaData = ModelMetadataProviders.Current.GetMetadataForType(() => _modelConstructor.Invoke(_params), _modelType); 
     var _bindingContext = new ModelBindingContext() { ModelName = _modelName, ValueProvider = dictionary, ModelMetadata = _modelMetaData }; 
     DefaultModelBinder _binder = new DefaultModelBinder(); 
     ControllerContext _controllerContext = new ControllerContext(); 
     T _object = (T)_binder.BindModel(_controllerContext, _bindingContext); 

     return _object; 
    } 
} 

例約定模型綁定:

public partial class Person 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public Project Project { get; set; } 
    public List<Person> Friends { get; set; } 
} 

public partial class Project 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

select 
    1 as [Id] 
    , 'NitinJs' as [Name] 
    , 5 as [Project.Id] 
    , 'Model Binding' as [Project.Name] 
    , 2 as [Friends[0]].Id] 
    , 'John' as [Friends[0]].Name] 
    , 3 as [Friends[1]].Id] 
    , 'Jane' as [Friends[1]].Name] 

現在,你需要將讀取你的數據結果,並將其綁定到一個模型的方法:

/// <summary> 
/// Reads a record from a SqlDataReader, binds it to a model, and adds the object to the results parameter 
/// </summary> 
/// <typeparam name="T"></typeparam> 
/// <param name="reader"></param> 
/// <param name="modelName"></param> 
/// <param name="results"></param> 
private void ReadAs<T>(SqlDataReader reader, string modelName, List<T> results, string commandText) 
{ 
    Dictionary<string, object> _result = new Dictionary<string, object>(); 
    for (int i = 0; i < reader.FieldCount; i++) 
    { 
     string _key = modelName + "." + reader.GetName(i); 
     object _value = reader.GetValue(i); 

     if (_result.Keys.Contains(_key)) // Dictionaries may not have more than one instance of a key, but a query can return the same column twice 
     {         // Since we are returning a strong type, we ignore columns that exist more than once. 
      throw new Exception("The following query is returning more than one field with the same key, " + _key + ": " + commandText); // command.CommandText 
     } 

     _result.Add(_key, _value); 
    } 

    T _object = new ModelBinder().BindModel<T>(_result); 

    if (_object != null) 
    { 
     results.Add((T)_object); 
    }   
} 

接下來,你需要一種獲得與你的數據庫的開放連接的方式(注意:你可能想從你的配置中獲取_dbConnectionString):

public SqlConnection GetOpenConnection() 
{ 
    _sqlConnection = new SqlConnection(_dbConnectionString); 
    _sqlConnection.Open(); 
    return _sqlConnection; 
} 

最後,你需要連接到你的數據庫,讓您的結果集:

/// <summary> 
/// Executes a SqlCommand that expects four result sets and binds the results to the given models 
/// </summary> 
/// <typeparam name="T1">Type: the type of object for the first result set</typeparam> 
/// <typeparam name="T2">Type: the type of object for the second result set</typeparam> 
/// <returns>List of Type T: the results in a collection</returns> 
public void ExecuteAs<T1, T2>(SqlCommand command, List<T1> output1, List<T2> output2) 
{ 
    string _modelName1 = typeof(T1).Name; 
    string _modelName2 = typeof(T2).Name; 
    string _commandText = command.CommandText; 

    using (SqlConnection connection = GetOpenConnection()) 
    { 
     using (command) 
     { 
      command.Connection = connection; 
      command.CommandTimeout = _defaultCommandTimeout; 
      using (SqlDataReader reader = command.ExecuteReader()) 
      { 
       while (reader.Read())            // Call Read before accessing data. 
       { 
        ReadAs<T1>(reader, _modelName1, output1, _commandText); 
       } 

       reader.NextResult(); 

       while (reader.Read())            // Call Read before accessing data. 
       { 
        ReadAs<T2>(reader, _modelName2, output2, _commandText); 
       } 
      } // end using reader 
     } // end using command 
    } // end using connection 
} 

那麼你的選擇方法看起來更像是這樣的:

public void SelectInto<SP_SearchEntity_Result, int>(int PageIndex, int PageSize, List<KeyValuePair<string, string>> SearchBy, List<KeyValuePair<string, System.Data.SqlClient.SortOrder>> SortBy, List<<SP_SearchEntity_Result> result1, List<int> result2) 
{ 
    SqlCommand command = new SqlCommand("SP_SearchEntity"); 
    command.CommandType = System.Data.CommandType.StoredProcedure; 
    command.Parameters.Add("PageIndex", SqlDbType.Int).Value = PageIndex; 
    command.Parameters.Add("SearchTableVar", SqlDbType.Xml).Value = SearchBy.ToXml(); 

    List<KeyValuePair<string, string>> SortByCastToString = // modify your ToDataTable method so you can pass a List<KeyValuePair<string, string>> for SortBy 
    command.Parameters.Add("SortTableVar", SqlDbType.Xml).Value = SortByCastToString.ToXml(); 

    ExecuteAs<SP_SearchEntity_Result, int>(command, result1, result2); 
} 

public void SomeCallingMethod() 
{ 
    List<SP_SearchEntity_Result> _results = new List<SP_SearchEntity_Result>{}; 
    List<int> _counts = new List<int>{}; 
    // ... 
    // setup your SearchBy and SortBy 
    // ... 

    SelectInto<SP_SearchEntity_Result, int>(1, 20, SearchBy, SortBy, _results, _counts); 
} 
+0

你能告訴我如何處理存儲過程的兩個輸出到對象中? –

5

將表值參數傳遞到SQL Server有許多要求/限制。見例如「傳遞一個表值參數存儲過程」下的示例:

然後,該代碼定義了一個SqlCommand,所述CommandType屬性設置爲StoredProcedure。通過使用AddWithValue方法填充SqlParameter,並且將SqlDbType設置爲Structured

而這只是用AddWithValue記是不夠的 - 在SqlDbType必須改變以Structured

我相信ExecuteSprocAccessor方法不執行此改變(或可能,如在一些其它實施例,其中該TypeName必須被設定到表類型的名稱)。我無法通過企業庫源代碼來追查這一切,但由於我無法在解決方案的任何位置找到「結構化」一詞,這就是我得出這一結論的原因。所以,如果你想使用TVP,我認爲你必須放棄企業庫,並使用SqlClient類型自己寫數據訪問代碼。 (既然你使用TVP,你已經放棄了切換到不同RDBMS的可能性)。

0

臺灣居民入境許可證作爲參數傳遞給存儲過程使用企業庫數據訪問應用程序塊v6.0.1304對我來說工作得很好。我的C#代碼如下所示:

public static DataSet SomeHelperMethod(DataTable tvp1, DataTable tvp2) 
{ 
    DbCommand cmd = <SqlDatabase>.GetStoredProcCommand("StoredProcName"); 

    SqlParameter p1 = new SqlParameter("@p1", tvp1); 
    p1.SqlDbType = SqlDbType.Structured; 
    cmd.Parameters.Add(p1); 

    SqlParameter p2= new SqlParameter("@p2", tvp2); 
    p2.SqlDbType = SqlDbType.Structured; 
    cmd.Parameters.Add(p2); 

    return <SqlDatabase>.ExecuteDataSet(cmd); 
}