SIGN UP MEMBER LOGIN:    
ARTICLE

How to: Implement UPSERT () Method in C#

Posted by Balakrushna Swain Articles | SQL August 23, 2010
In this article you will learn how to Implement UPSERT () Method in C#.
Reader Level:
Download Files:
 

Upsert is a Composite word; combines Insert and Update. As per the name itself Upsert defines that, using this single method we can either Insert a record in to Table or we can Update the required record by providing the new values. So using this single method we can get these advantages.

  1. Instead of writing two methods separately we can aggregate the code into single method.
  2. No need to create to separate Stored Procedures for Insert and Update.
  3. Easy to Debug and Maintain as Upsert is the single point entry for both the functionality.
  4. Single point Validation.
  5. Less Coding.

Here is the step by step to implement the Upsert Method.

Step 1:

Create a Stored procedure for your Table with name like spUPSERT_EMP_MAST. Here I assumed that my table name is EMP_MAST and my Table structure is like

CMP_ID

int

Auto Number

CMP_NAME

varchar(100)

 

CMP_LOGO

varchar(100)

 

CMP_PNCH_LINE

varchar(200)

 

CMP_ADRS1

varchar(200)

 

CMP_ADRS2

varchar(200)

 

CMP_PHN

varchar(30)

 

CMP_FAX

varchar(30)

 

CMP_URL

varchar(100)

 

CMP_EMAIL

varchar(100)

 

So the Upsert stored procedure for this table as:

CREATE PROCEDURE sp_UPSERT_CMP_MAST

@CMP_ID       int output,
@CMP_NAME     varchar(100),
@CMP_LOGO     varchar(100),
@CMP_PNCH_LINE       varchar(200),
@CMP_ADRS1    varchar(200),
@CMP_ADRS2    varchar(200),
@CMP_PHN      varchar(30),
@CMP_FAX      varchar(30),
@CMP_URL      varchar(100),
@CMP_EMAIL    varchar(100),
@FLAG         bit           -- Insert/Update Flag
 
AS
BEGIN
       -- If the Insert/Update Flag is True, then it will insert a record.
       IF(@FLAG = 1)
       BEGIN
              INSERT INTO CMP_MAST(CMP_NAME
                     ,CMP_LOGO
                     ,CMP_PNCH_LINE
                     ,CMP_ADRS1
                     ,CMP_ADRS2
                     ,CMP_PHN
                     ,CMP_FAX
                     ,CMP_URL
                     ,CMP_EMAIL)
              VALUES(@CMP_NAME
                     ,@CMP_LOGO
                     ,@CMP_PNCH_LINE
                     ,@CMP_ADRS1
                     ,@CMP_ADRS2
                     ,@CMP_PHN
                     ,@CMP_FAX
                     ,@CMP_URL
                     ,@CMP_EMAIL)
              -- Stores the last inserted Id from the Company Master Table to the Input-Output Variable..
              SET @CMP_ID = (SELECT TOP 1 @@IDENTITY FROM CMP_MAST)
       END
       ELSE          -- If the Flag is False then Update the Record.
       BEGIN
              UPDATE CMP_MAST SET CMP_NAME = @CMP_NAME
                     ,CMP_LOGO = @CMP_LOGO
                     ,CMP_PNCH_LINE = @CMP_PNCH_LINE
                     ,CMP_ADRS1 = @CMP_ADRS1
                     ,CMP_ADRS2 = @CMP_ADRS2
                     ,CMP_PHN = @CMP_PHN
                     ,CMP_FAX = @CMP_FAX
                     ,CMP_URL = @CMP_URL
                     ,CMP_EMAIL = @CMP_EMAIL
              WHERE CMP_ID = @CMP_ID
      
END
END
RETURN


Step 2:

Now inside you Data Access Layer (DAL) add a method like

/// <summary>
///
A single method to insert/Update the Company record.
/// </summary>
///
<param name="company">A Company object.</param>
///
<param name="upsertFlag">Insert/Update Flag; set it True to Insert and False to Update.</param>
///
<returns>The last Inserted/Updated Company Id.</returns>
public int Upsert(Company company, bool upsertFlag)
{
int result = 0;
try
       {
              SqlCommand sqlCommand = new SqlCommand("sp_UPSERT_CMP_MAST", sqlConnection);
sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
 
              // Input / Output SQL parameter of Company Id.
              SqlParameter identity = new SqlParameter("@CMP_ID", 0);
              identity.Direction = System.Data.ParameterDirection.InputOutput;
 
              sqlCommand.Parameters.Add(identity);
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_NAME", company.Name));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_LOGO", company.Logo));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_PNCH_LINE", company.PunchLine));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_ADRS1", company.AddressLine1));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_ADRS2", company.AddressLine2));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_PHN", company.Phone));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_FAX", company.Fax));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_URL", company.WebURL));
              sqlCommand.Parameters.Add(new SqlParameter("@CMP_EMAIL", company.Email));
              sqlCommand.Parameters.Add(new SqlParameter("@FLAG", upsertFlag));
 
              sqlConnection.Open();
              sqlCommand.ExecuteNonQuery();
 
              // Return the last inserted Id or updated Id.
              result = Convert.ToInt32(sqlCommand.Parameters["@CMP_ID"].Value);
sqlConnection.Close();
 
sqlCommand.Dispose();
}
catch (Exception)
       {
              throw;
}
 
return result;
}


Step 3:

Now Inside your Business Logic Layer add these two methods

public int AddCompany(Company company)
 {
     try
     {
         CompanyData companyData = new CompanyData();
         return companyData.Upsert(company, true);
     }
     catch (Exception)
     {
         throw;
     }
}

public int EditCompany(Company company)
{
     try
     {
         CompanyData companyData = new CompanyData();
         return companyData.Upsert(company, false);
     }
     catch (Exception)
     {
         throw;
     }
}


Now from the Presentation layer you can call the Add() and Edit()methods of Business Logic Layer for Insert and/or Update of the Company Data.

Note: This Article also gives you an idea "How to Implement Auto Identity in your Table and Get the last Inserted value by C# Code". See the DAL, you can easily understand the process.
 

Login to add your contents and source code to this article
share this article :
post comment
 

Yes, for a part I agree with your explanation. I really wonder for what number of records and proper indexing and profiling the execution plan will become a problem. However our approach also has some other advantages, the most important is preventing the insertion of doubles without coding this in the source code.

So far we didn't use our upsert in databases with tables that had extremely many records in them, but in moderate sized tables it worked just fine.

But as always, you must use the correct tools for the job, so both your approach and ours are suitable, depending on the requirements of the solution.

Regards,

Jan

Posted by Jan Pieters Aug 23, 2010

Hey Jan,

It's really nice to hear from you...

Also thanks a lot for providing such a great common functionality. But Jan, I want to say like it depends upon the requirement. If the number of record is less, then your code stuff will work fine. But think if the number of record is thousands or more can you think about the execution plan; you are searching the record first and then inserting/updating the record. Can you imagine how costly the process will be?

It’s a matter of bit parameter. I have given a generic model; you can add/modify any code stuff as per your requirement.

Any way thanks again for your feedback.

Posted by Balakrushna Swain Aug 23, 2010

When we make an upsert stored procedure we let the stored procedure decide if an insert or an update has to be performed. An example:


SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS OFF 
GO

use Reperion
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[TimeZones]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[TimeZones]
GO

CREATE TABLE [dbo].[TimeZones](
[TimeZoneId] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS PRIMARY KEY NOT NULL,
[GMTOffset] [decimal] (3,2),
[DSTOffset] [decimal] (3,2)
) ON [PRIMARY]
GO

CREATE PROCEDURE SPUpsertTimeZone(
@TimeZoneId varchar(50),
@GMTOffset decimal(3,2),
@DSTOffset decimal(3,2)
 ) AS

IF EXISTS (SELECT * FROM TimeZones WHERE TimeZoneId = @TimeZoneId)
  BEGIN
    -- UPDATE
    UPDATE TimeZones
SET
GMTOffset=@GMTOffset,
DSTOffset=@DSTOffset
WHERE TimeZoneId = @TimeZoneId
RETURN 1
   END
ELSE
  BEGIN
INSERT INTO TimeZones
(
TimeZoneId,
GMTOffset,
DSTOffset 
VALUES
@TimeZoneId,
@GMTOffset ,
@DSTOffset
)
    RETURN 0
  END
GO

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

This makes the use of the flag parameter unnecessary.

Regards,

Jan 

Posted by Jan Pieters Aug 23, 2010
6 Months Free & No Setup Fees ASP.NET Hosting!
Become a Sponsor
PREMIUM SPONSORS
  • Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
    Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites - Click Here!
6 Months Free & No Setup Fees ASP.NET Hosting!
Become a Sponsor