2013-08-16 35 views
10

Tôi có một thủ tục được lưu InsertCars chấp nhận danh sách loại bảng do người dùng định nghĩa CarType.Gọi thủ tục lưu sẵn từ người lập bản đồ chấp nhận danh sách loại bảng do người dùng định nghĩa

CREATE TYPE dbo.CarType 
AS TABLE 
(
    CARID int null, 
    CARNAME varchar(800) not null, 
); 

CREATE PROCEDURE dbo.InsertCars 
    @Cars AS CarType READONLY 
AS 
-- RETURN COUNT OF INSERTED ROWS 
END 

Tôi cần gọi thủ tục lưu trữ này từ Dapper. Tôi googled nó và tìm thấy một số giải pháp.

var param = new DynamicParameters(new{CARID= 66, CARNAME= "Volvo"}); 

var result = con.Query("InsertCars", param, commandType: CommandType.StoredProcedure); 

Nhưng tôi nhận được một lỗi:

Procedure or function InsertCars has too many arguments specified

thủ tục Cũng lưu trữ InsertCars trả về số lượng các hàng chèn; Tôi cần có giá trị này.

Gốc của sự cố ở đâu?

Vấn đề của tôi cũng là tôi có ô tô trong danh sách chung List<Car> Cars và tôi muốn chuyển danh sách này để lưu trữ thủ tục. Nó tồn tại cách thanh lịch như thế nào để làm điều đó?

public class Car 
{ 
    public CarId { get; set; } 
    public CarName { get; set; } 
} 

Cảm ơn bạn đã giúp đỡ

EDITED

tôi tìm thấy giải pháp

Does Dapper support SQL 2008 Table-Valued Parameters?

hoặc

Does Dapper support SQL 2008 Table-Valued Parameters 2?

Vì vậy, tôi cố gắng làm ngu ngốc của riêng lớp helper

class CarDynamicParam : Dapper.SqlMapper.IDynamicParameters 
{ 
    private Car car; 

    public CarDynamicParam(Car car) 
    { 
     this.car = car; 
    } 

    public void AddParameters(IDbCommand command, SqlMapper.Identity identity) 
    { 
     var sqlCommand = (SqlCommand)command; 

     sqlCommand.CommandType = CommandType.StoredProcedure; 

     var carList = new List<Microsoft.SqlServer.Server.SqlDataRecord>(); 

     Microsoft.SqlServer.Server.SqlMetaData[] tvpDefinition = 
                   { 

                    new Microsoft.SqlServer.Server.SqlMetaData("CARID", SqlDbType.Int), 
                    new Microsoft.SqlServer.Server.SqlMetaData("CARNAME", SqlDbType.NVarChar, 100), 
                   }; 

     var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvpDefinition); 
     rec.SetInt32(0, car.CarId); 
     rec.SetString(1, car.CarName); 

     carList.Add(rec); 

     var p = sqlCommand.Parameters.Add("Cars", SqlDbType.Structured); 
     p.Direction = ParameterDirection.Input; 
     p.TypeName = "CarType"; 
     p.Value = carList; 
    } 
} 

Sử dụng

var result = con.Query("InsertCars", new CarDynamicParam(car), commandType: CommandType.StoredProcedure); 

tôi nhận được ngoại lệ

When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id.

StackTrace:

at Dapper.SqlMapper.GetDynamicDeserializer(IDataRecord reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 1308 
    at Dapper.SqlMapper.GetDeserializer(Type type, IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 1141 
    at Dapper.SqlMapper.<QueryInternal>d__d`1.MoveNext() in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 819 
    at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) 
    at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) 
    at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 770 
    at Dapper.SqlMapper.Query(IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 715 

Có gì sai?

FIXED:

Gọi con.Execute thay con.Query

Trả lời

6

My problem is also that I have cars in generic list List Cars and I want pass this list to store procedure. It exist elegant way how to do it ?

Bạn cần phải chuyển đổi danh sách xe generic của bạn vào một DataTable và sau đó vượt qua nó để StoredProcedure. Một điểm cần lưu ý là thứ tự các trường của bạn phải là giống như được định nghĩa trong bảng loại người dùng được xác định trong cơ sở dữ liệu. Nếu không dữ liệu sẽ không lưu đúng cách. Và nó cũng phải có cùng số cột.

Tôi sử dụng phương pháp này để chuyển đổi Danh sách thành DataTable. Bạn có thể gọi nó như yourList.ToDataTable()

public static DataTable ToDataTable<T>(this List<T> iList) 
    { 
     DataTable dataTable = new DataTable(); 
     PropertyDescriptorCollection propertyDescriptorCollection = 
      TypeDescriptor.GetProperties(typeof(T)); 
     for (int i = 0; i < propertyDescriptorCollection.Count; i++) 
     { 
      PropertyDescriptor propertyDescriptor = propertyDescriptorCollection[i]; 
      Type type = propertyDescriptor.PropertyType; 

      if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) 
       type = Nullable.GetUnderlyingType(type); 


      dataTable.Columns.Add(propertyDescriptor.Name, type); 
     } 
     object[] values = new object[propertyDescriptorCollection.Count]; 
     foreach (T iListItem in iList) 
     { 
      for (int i = 0; i < values.Length; i++) 
      { 
       values[i] = propertyDescriptorCollection[i].GetValue(iListItem); 
      } 
      dataTable.Rows.Add(values); 
     } 
     return dataTable; 
    } 
+0

đầu tiên tham số qua vấn đề ist thủ tục lưu trữ. Tôi nghĩ rằng số lượng và thứ tự là đúng. Tôi kiểm tra thủ tục của tôi thông qua MS SQL Studio và nó hoạt động tốt. Loại bảng do người dùng xác định chỉ có 2 cột, CARID và CARNAME. Trong DynamicParamater tôi chỉ thiết lập hai cột này. Vì vậy, tôi không hiểu nơi có thể là vấn đề? – imodin

+0

@Wobe tôi đã đề cập đến cách hoàn chỉnh để thực hiện việc này. cho các tham số kiểu bảng tôi chuyển datatable như một tham số và không phải là một tham số dyamicparameter – Ehsan

0

Các giải pháp khác sẽ gọi nó như

var param = new DynamicParameters(new{CARID= 66, CARNAME= "Volvo"}); 

var result = con.Query<dynamic>("InsertCars", param); 

Remove này: mới CarDynamicParam (ô tô), CommandType: CommandType.StoredProcedure

Sử dụng tham số của loại bảng trực tiếp, nó sẽ hoạt động.

Nếu bạn có thể sử dụng Datatable (lõi .net không hỗ trợ nó), sau đó nó rất dễ dàng.

Tạo DataTable -> Thêm cột bắt buộc để phù hợp với loại bảng của bạn -> Thêm hàng được yêu cầu. Cuối cùng chỉ cần gọi nó bằng cách sử dụng dapper như thế này.

var result = con.Query<dynamic>("InsertCars", new{paramFromStoredProcedure=yourDataTableInstance}, commandType: CommandType.StoredProcedure); 
+1

Bạn làm gì trong .Net Core sau đó? –

+1

Phần đầu tiên của giải pháp là dành cho .Net Core –

2

Tôi biết đây là một chút cũ, nhưng tôi nghĩ tôi sẽ đăng bài này anyway vì tôi tìm cách để làm cho điều này dễ dàng hơn một chút. Tôi hy vọng tôi đã làm như vậy với một gói NuGet tôi tạo mà sẽ cho phép mã như:

public class CarType 
{ 
    public int CARID { get; set; } 
    public string CARNAME{ get; set; } 
} 

var cars = new List<CarType>{new CarType { CARID = 1, CARNAME = "Volvo"}}; 

var parameters = new DynamicParameters(); 
parameters.AddTable("@Cars", "CarType", cars) 

var result = con.Query("InsertCars", parameters, commandType: CommandType.StoredProcedure); 

NuGet gói: https://www.nuget.org/packages/Dapper.ParameterExtensions/0.2.0 Vẫn còn trong giai đoạn đầu của nó như vậy có thể không làm việc với tất cả mọi thứ!

Vui lòng đọc README và cảm thấy tự do đóng góp trên GitHub: https://github.com/RasicN/Dapper-Parameters

+0

Cảm ơn NuGet! – optim1st

+0

bạn có biết bất kỳ thay thế lõi .net nào không? – FiringSquadWitness

+0

Tôi chưa cập nhật điều này để làm việc với lõi .net nghĩ rằng đó là nguồn mở nên bạn có thể thoải mái xem xét việc thêm vào đó. Nếu không có gì khác vui lòng thêm nó như là một vấn đề để theo dõi nó và cuối cùng tôi sẽ xem xét cập nhật nó. – JCisar