[英]Database name is not allowed with a table-valued parameter
当我调用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
将表值参数传递给SQL Server有许多要求/限制。 例如,参见“将表值参数传递给存储过程”下的示例:
然后代码定义
SqlCommand
,将CommandType
属性设置为StoredProcedure
。 使用AddWithValue
方法填充SqlParameter
,并将SqlDbType
设置为Structured
。
请注意,仅使用AddWithValue
是不够的 - 必须将SqlDbType
更改为Structured
。
我相信 ExecuteSprocAccessor
方法没有执行此更改(或者可能,如在其他一些示例中,其中TypeName
必须设置为表类型的名称)。 我不能通过企业库源代码追逐这一切,但由于我在解决方案的任何地方都找不到“结构化”这个词,这就是我得出这个结论的原因。
因此,如果您想使用TVP,我认为您必须放弃企业库并使用SqlClient
类型SqlClient
编写数据访问代码。 (因为你正在使用TVP,你已经放弃了切换到不同RDBMS的可能性)。
我发现存储过程参数的xml数据类型更容易使用。 您可以将它们转换为XML,而不是将参数转换为DataTables,以用于以下示例:
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 Serializer:
// 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。 如果您真的只是返回已返回对象的计数,则可以在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 }
然后你的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); }
使用企业库数据访问应用程序块v6.0.1304,TVP作为存储过程的参数对我来说很有用。 我的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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.