2015-11-18 7 views
8

ho creato un tipo di tabella definito dall'utente in SQL Server:Passo valore della tabella tipo a SQL Server stored procedure tramite Entity Framework

CREATE TYPE dbo.TestType AS TABLE 
(
    ColumnA int, 
    ColumnB nvarchar(500) 
) 

E sto utilizzando una stored procedure per inserire i record nel database:

create procedure [dbo].[sp_Test_CustomType] 
    @testing TestType READONLY 
as 
    insert into [dbo].[myTable] 
     select ColumnA, ColumnB 
     from @testing 

E mi piacerebbe utilizzare EF per eseguire questa stored procedure, ma ecco il problema: come posso passare una tabella definita dall'utente alla stored procedure?

Ho provato ad aggiungere la procedura memorizzata al modello, ma non riesco a trovare la stored procedure desiderata nel contesto aggiornato.

Quello che sto cercando di fare è di eseguire un inserimento di massa ad un tavolo, ecco il metodo che sono attualmente in uso:

List<items> itemToInsertToDB = //fetchItems; 

foreach(items i in itemToInsertToDB) 
{ 
    context.sp_InsertToTable(i.ColumnA, i.ColumnB) 
} 

Attualmente, io uso un ciclo foreach per scorrere l'elenco per inserire elementi in DB, ma se l'elenco contiene molti elementi, si verificherà un problema di prestazioni, quindi, sto pensando di passare una lista alla stored procedure e inserire l'inserto all'interno.

Quindi come risolvere questo problema? o ci sono modi migliori per farlo?

+0

http: //www.c-sharpcorner.it/UploadFile/78607b/using-table-valued-parameters-in-entity-framework/ – niksofteng

risposta

6

Diciamo che si desidera inviare una tabella con una singola colonna di GUID.

Per prima cosa è necessario creare una struttura utilizzando SqlMetaData che rappresenta lo schema della tabella (colonne).

var tableSchema = new List<SqlMetaData>(1) 
{ 
    new SqlMetaData("Id", SqlDbType.UniqueIdentifier) 
}.ToArray(); 

successivo si crea un elenco di record che soddisfano lo schema utilizzando SqlDataRecord.

var tableRow = new SqlDataRecord(tableSchema); 
tableRow.SetGuid(0, Guid.NewGuid()); 
var table = new List<SqlDataRecord>(1) 
{ 
    tableRow 
}; 

quindi creare la SqlParameter:

var parameter = new SqlParameter(); 
parameter.SqlDbType = SqlDbType.Structured; 
parameter.ParameterName = "@UserIds"; 
parameter.Value = table; 

var parameters = new SqlParameter[1] 
{ 
    parameter 
}; 

Poi basta chiamare la stored procedure utilizzando il Database.SqlQuery.

IEnumerable<ReturnType> result; 
using (var myContext = new DbContext()) 
{ 
    result = myContext.Database.SqlQuery<User>("GetUsers", parameters) 
    .ToList();   // calls the stored procedure 
    // ToListAsync(); // Async 
{ 

In SQL Server, creare il tipo definito dall'utente Tabella (I loro suffisso con TTV, ping-tipizzati Value):

CREATE TYPE [dbo].[UniqueidentifiersTTV] AS TABLE(
    [Id] [uniqueidentifier] NOT NULL 
) 
GO 

quindi specificare il tipo come parametro (non dimenticate, I valori del tipo di tabella devono essere di sola lettura!):

CREATE PROCEDURE [dbo].[GetUsers] (
    @UserIds [UniqueidentifiersTTV] READONLY 
) AS 
BEGIN 
    SET NOCOUNT ON 

    SELECT u.* -- Just an example :P 
    FROM [dbo].[Users] u 
    INNER JOIN @UserIds ids On u.Id = ids.Id 
END 
+0

Da dove proviene the.SetGuid? –

+0

[SqlDataRecord.SetGuid()] (https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.server.sqldatarecord.setguid (v = vs.110) .aspx) –

+1

1) mouseover 'risultato 'nel debugger verrà lanciata un'eccezione" stored procedure deve dichiarare la variabile scalare "perché sta tentando di valutare il parametro due volte. Usa '.ToList()' (come sopra) per evitare questo. 2) Stavo ricevendo un'eccezione "Il parametro del tipo di tabella '@table' deve avere un nome di tipo valido." che è stato risolto specificando il tipo di parametro: 'parameter.TypeName =" dbo.TestType "' (con prefisso "dbo") – BurnsBA

3

Suggerisco di non utilizzare Stored Procedure per inserire dati bulk, ma semplicemente fare affidamento sul meccanismo di inserimento di Entity Framework.

List<items> itemToInsertToDB = //fetchItems; 
foreach(items i in itemToInsertToDB) 
{ 
    TestType t = new TestType() { ColumnA = i.ColumnA, ColumnB = i.ColumnB }; 
    context.TestTypes.Add(t); 
} 
context.SaveChanges(); 

quadro Entità intelligentemente effettuare quelle inserimento in un'unica operazione e (solitamente) in esecuzione singola query, che sarà quasi uguale a eseguire stored procedure. Questo è meglio piuttosto che basarsi su stored procedure solo per inserire una grande quantità di dati.

+0

Grazie per il tuo suggerimento, so che il framework Entity ha una funzione chiamata "bulkinsert" https://efbulkinsert.codeplex.com/, ma mi è stato richiesto di utilizzare la stored procedure per una manutenzione più semplice. – User2012384

+0

EF funziona bene a meno che non ci si trovi in ​​uno scenario di transazione distribuito molto complicato utilizzando gli ambiti di transazione. – Chazt3n

+0

EF non è così intelligente. Salva modifiche esegue una transazione, ma emette singole istruzioni di inserimento per ogni entità inserita. Ecco perché BulkInsert è una libreria separata, a pagamento, anche se esistono versioni precedenti e gratuite. Per impostazione predefinita, EF invierà una dichiarazione di inserimento separata per ogni articolo inserito. La performance è orribile. Per gli inserimenti di massa, utilizzare SqlBulkCopy o utilizzare la versione gratuita dell'estensione di inserimento di massa. – Triynko

Problemi correlati