3

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;
}



ストアド プロシージャは結果として 2 つのテーブルを返します

SQL プロシージャの定義

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
4

3 に答える 3

5

テーブル値パラメーターをSQL Serverに渡すには、いくつかの要件/制限があります。たとえば、「テーブル値パラメーターをストアド プロシージャに渡す」の例を参照してください。

次に、コードは を定義し、プロパティをSqlCommandに設定します。メソッドを使用して が読み込まれ、が に設定されます。CommandTypeStoredProcedureSqlParameterAddWithValueSqlDbTypeStructured

AddWithValueを使用するだけでは不十分であることに注意してください-SqlDbTypeを に変更する必要がありStructuredます。

メソッドがこの変更を実行していないと思いExecuteSprocAccessorます(または、他の例のように、テーブル型の名前に設定する必要がある場合もあります)。エンタープライズ ライブラリのソース コード全体を通してこれを追跡することはできませんが、ソリューションのどこにも「構造化」という言葉が見つからないため、この結論に至りました。TypeName

したがって、TVP を使用する場合は、Enterprise Library を放棄して、型を使用してデータ アクセス コードを自分で作成する必要があると思いますSqlClient。(TVP を使用しているので、とにかく別の RDBMS に切り替える可能性を既に放棄しています)。

于 2012-09-29T11:07:15.717 に答える
1

ストアドプロシージャパラメータのxmlデータ型の方が使いやすいことがわかりました。次の例では、パラメーターをDataTablesにキャストするのではなく、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

XMLとしてシリアル化された後のKeyValuePairとクエリのサンプルを次に示します。

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();
}


編集:複数の結果セットを返し、それらをオブジェクトにバインドする

その方法を説明しますが、模擬SQLに基づいて、それが何をしたいのかわかりません。返されたオブジェクトの数を実際に返すだけの場合は、IQuery可能になった後で結果を数えることができます。

まず、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
}   

次に、selectメソッドは次のようになります。

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);
}
于 2012-10-06T06:51:09.913 に答える
0

ストアド プロシージャへのパラメーターとしての TVP は、Enterprise Library Data Access Application Block 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);
}
于 2017-01-18T17:17:11.873 に答える