Showing posts with label Dapper. Show all posts
Showing posts with label Dapper. Show all posts

Friday, November 29, 2019

Bulk Insert With Dapper - The Right One

One of my favorite online trainers, Tim Corey, introduced Dapper for me two years ago. It was only the last week when I stumbled upon his advanced Dapper video, and implemented the correct version for a bulk insert, using this micro-ORM.

Prerequisites:
- install Dapper nuget package;
- intermediate C#/with MS SQL database;

The task of our bulk insert will be to insert the following data into an MS SQL database:

a. Table structure in SQL, defined by the query:

CREATE TABLE [dbo].[InvoiceSummary](
[id] [int] IDENTITY(1,1) NOT NULL,
[Inv_Number] [int] NOT NULL,
[IssueDate] [datetime] NOT NULL,
[BillingCode] [nvarchar](100) NOT NULL,
[EntityName] [nvarchar](200) NOT NULL,
[BUName] [nvarchar](100) NOT NULL,
[Value] [float] NOT NULL,
[VAT] [float] NOT NULL,
[TotalValue] [float] NOT NULL,
[Currency] [nvarchar](50) NOT NULL,
[ExchangeRate] [float] NOT NULL,
[Entity_Id] [int] NOT NULL,
[BU_id] [int] NOT NULL,
[UpdatedBy] [nvarchar](100) NOT NULL,
[UpdatedAt] [datetime] NOT NULL,
[PeriodID] [nvarchar](50) NOT NULL,
[Comments] [nvarchar](200) NOT NULL,
[Comment] [nvarchar](200) NOT NULL,
[Status] [nvarchar](50) NOT NULL,
[AttentionOf] [nvarchar](300) NOT NULL,
[CC] [nvarchar](300) NULL,
[HFMCode] [nvarchar](100) NULL,
 CONSTRAINT [PK_InvoiceSummary] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[InvoiceSummary] ADD  CONSTRAINT [DF_InvoiceSummary_UpdatedAt]  DEFAULT (getdate()) FOR [UpdatedAt]
GO

b. There is a type defined, for the table-valued parameter, see SQL statement below:

CREATE TYPE [dbo].[BasicUDT] AS TABLE(
[id] [int] NOT NULL,
[Inv_Number] [int] NOT NULL,
[IssueDate] [datetime] NOT NULL,
[BillingCode] [nvarchar](100) NOT NULL,
[Entity_Id] [int] NOT NULL,
[BU_id] [int] NOT NULL,
[EntityName] [nvarchar](200) NOT NULL,
[BUName] [nvarchar](100) NOT NULL,
[Value] [float] NOT NULL,
[VAT] [float] NOT NULL,
[TotalValue] [float] NOT NULL,
[Currency] [nvarchar](50) NOT NULL,
[ExchangeRate] [float] NOT NULL,
[Comments] [nvarchar](200) NOT NULL,
[AttentionOf] [nvarchar](300) NOT NULL,
[CC] [nvarchar](300) NULL,
[HFMCode] [nvarchar](100) NULL,
[UpdatedBy] [nvarchar](100) NOT NULL,
[UpdatedAt] [datetime] NOT NULL,
[PeriodID] [nvarchar](50) NOT NULL,
[Comment] [nvarchar](200) NOT NULL,
[Status] [nvarchar](50) NOT NULL
)
GO

c. Stored procedure, to insert into our table, using table valued parameter:

CREATE procedure [dbo].[spInvoiceSummaryInsertSet]
@invsummary BasicUDT readonly
as
begin
set nocount on;
INSERT INTO [dbo].[InvoiceSummary]
           ([Inv_Number]
           ,[IssueDate]
           ,[BillingCode]
           ,[Value]
           ,[VAT]
           ,[TotalValue]
           ,[Currency]
           ,[ExchangeRate] 
           ,[Entity_Id]
           ,[BU_id]           
           ,[UpdatedBy], UpdatedAt
           ,[PeriodID]
           ,[Comment]
           ,[Status],
    [Comments]           
           ,[AttentionOf]
           ,[CC]
           ,[HFMCode],
   [EntityName],
   [BUName]
   )
SELECT [Inv_Number]
           ,[IssueDate]
           ,[BillingCode]
           ,[Value]
           ,[VAT]
           ,[TotalValue]
           ,[Currency]
           ,[ExchangeRate] 
           ,[Entity_Id]
           ,[BU_id]           
           ,[UpdatedBy], CURRENT_TIMESTAMP
           ,[PeriodID]
           ,[Comment]
           ,[Status],
    [Comments]           
           ,[AttentionOf]
           ,[CC]
           ,[HFMCode],
   [EntityName],
   [BUName]
from @invsummary


end;

d. Our original list in C# will contain the values we need to insert into the table:

// myInvList is a list of InvoiceSummary items defined by the class above at a)
InvoiceSummaryInsertSet(myInvList)

e. Our list is getting inserted into SQL Server DB using the table valued parameter

        public void InvoiceSummaryInsertSet(List<InvoiceSummary> myInvList)        {

            var dt = new ExcelServices().ConvertToDataTable(myInvList);

            using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(connectionString))
            {
                var p = new
                {
                    invsummary = dt.AsTableValuedParameter("BasicUDT")
                };

                connection.Execute("dbo.spInvoiceSummaryInsertSet ", p, commandType: CommandType.StoredProcedure);
            }
        }

f. helper function to transform a list into datatable, used above, is implemented as below:

        public System.Data.DataTable ConvertToDataTable<T>(IList<T> data)

        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));

            System.Data.DataTable table = new System.Data.DataTable();

            foreach (PropertyDescriptor prop in properties)
            {
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }

            foreach (T item in data)

            {

                DataRow row = table.NewRow();

                foreach (PropertyDescriptor prop in properties)
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;             

                }

                table.Rows.Add(row);
            }

            return table;

        }

That's all, it works ! Hopefully it helped some of you.

Thursday, November 7, 2019

Bulk Insert in Dapper into MS SQL

I am collecting costs by employee from 6 sources and then insert them into a a GeneratedDetail table. But the collected data has 1000+ rows, and the insertion one by one row is very slow. I needed to find a bulk insert solution.
SQL Server DevOps
I am using Dapper as ORM in my C# project, and MS SQL Database.
According to research, my approach would be as below. It works now quite fast, I am satisfied with the speed of execution. Feel free to comment and propose a better solution.
note: spGeneratedDetailInsert  is a stored procedure with many parameters.

        public  void GeneratedDetailInsertBulk1(List<GeneratedDetail> DetailList)
        {
            using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(connectionString))
            {
                var sql = "";           
                foreach (var mydet in DetailList)
                {               
                    sql = sql + "Exec spGeneratedDetailInsert '" + mydet.BillingCode + "', " +
                        mydet.Emp_id + ", '" +
                        mydet.Emp_name + "', " +
                        mydet.HrId + ", " +
                        mydet.Entity_id + ", " +
                        mydet.BU_id + ", '" +
                        mydet.Currency + "', '" +
                        mydet.Item + "', " +
                        mydet.ExchangeRate + ", " +
                        mydet.InitValueRON + ", " +
                        mydet.InitValueCurr + ", " +
                        mydet.MUValueCurr + ", " +
                        mydet.TotalRon + ", '" +
                        mydet.ServiceType + "', '" +
                        mydet.CostCategory + "', '" +
                        mydet.UpdatedBy + "', '" +
                        mydet.UpdatedAt + "', '" +
                        mydet.PeriodId + "' ; ";                                   
                }
                 connection.Execute(sql);
            }
        }
Note: I am not using Dapper Plus, and I don't want to.

Dapper Plus | Learn how to use Dapper Bulk Insert with ...

Tuesday, November 5, 2019

Lots of SQL today

As part of my big project, today I dealt with some table creation and query writing. Mentioned earlier, I use Dapper as ORM and this means no EF queries are generated automatically.
So all Insert, Update, Select etc queries have to be written manually and returned and mapped to POCO entities (simple classes) via Dapper.



I think 2 hours was spent today on this.

I created 3 big tables (10+ fields) and wrote all CRUD queries as mentioned above.

Then, using a helper class, the ORM mappings are written in one place, just to call these functions within the application, when needed.

Some demo, from Tim Corey: https://youtu.be/QVkpzuiiVtw

An the foundation, for Stored Procedures, I used this: https://youtu.be/Sggdhot-MoM

What do you think? IS EF much more efficient?

By the way, my small prototype project in .Net core Razor Pages uses EF Core, which I wanted to learn step by step to get accustomed to it. It is nice, too, I like it! For a simple web project, it is enough for my purposes.