Tuesday, January 12, 2021

How to Create a WhatsApp Bot using C#: A Complete Guide

Introduction

In this guide, we will tell you how to create a WhatsApp bot with C# by using our API WhatsApp gateway.

The bot will receive commands in the form of regular WhatsApp messages and respond to them. A test bot’s functionality will be limited to the following features:

  • Showing a welcome message in response to commands the bot doesn’t have and displaying the bot menu
  • Displaying the current chat ID (in private messages or group chats)
  • Sending files of different formats (pdf, jpg, doc, mp3, etc.)
  • Sending voice messages (*.ogg files)
  • Sending Geolocation
  • Creating a separate group chat between a user and a bot

For our bot, we will use the ASP.Net technology that allows for launching a server that will process user requests and respond to them.

Get free access to WhatsApp API

Chapter 1. Creating an ASP.Net Project

Open Visual Studio and create a project with the name "ASP.NET Core Web App".

Next, choose an empty project template. You can also choose an API template that already includes all the necessary controllers — then you will only have to edit them. In our example though, for illustrative purposes, we will develop the project from scratch.

Open the Startup.cs file and implement the Configure method:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
         if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

This will allow you to use navigation via controller. Next up, you’ll need to write the controller itself.

To do this, create a Controllers folder within the project — this is where you will create a WebHookController class.

The controller must inherit the ControllerBase class and be marked by the attributes

using Microsoft.AspNetCore.Mvc;
namespace WaBot.Controllers
    {
        [ApiController]
        [Route("/")]
        public class WebHookController : ControllerBase
            {

            }
    }

The Route attribute is responsible for the address which the controller will serve. Now specify the domain base path.

At this stage, the controller is practically ready. You need to add methods of working with API WA and other utility classes that you will use.


Phone Authorization

Now you are supposed to connect WhatsApp to your script so you could check the code while you are writing it. To do this, go to your User Account and get the QR code. Then open WhatsApp on your phone and go to Settings -> WhatsApp Web -> Scan the QR Code.


Chapter 2. The API Class

In this chapter, we will look at how to write the class responsible for communication with our API gateway. You can check the documentation here. Here is how you create the WaApi class:

public class WaApi
    {
        private string APIUrl = "";
        private string token = "";

        public WaApi(string aPIUrl, string token)
            {
                APIUrl = aPIUrl;
                this.token = token;
            }
    }

This class will contain the APIUrl and token fields that are necessary for working with API. You can get them in your user account.

The same applies to the constructor that assigns values to the fields and allows you to have several objects representing different bots in case you want to run multiple bots simultaneously.

The Method of Sending Requests

Now it’s time to add an async method to the class. This will be responsible for sending POST requests:

public async Task<string> SendRequest(string method, string data)
    {
        string url = $"{APIUrl}{method}?token={token}";

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(url);
            var content = new StringContent(data, Encoding.UTF8, "application/json");
            var result = await client.PostAsync("", content);
            return await result.Content.ReadAsStringAsync();
        }
    }

The method receives two arguments.

  •   method — the name of the method according to the documentation
  •   data — the JSON string for sending data

In the method, form a URL string where requests will be sent. Send a POST request to the address using System.Net.Http.HttpClient and return the server’s response.

The method allows you to build all the functionality necessary for the bot’s work.

Sending Messages

public async Task<string> SendMessage(string chatId, string text)
    {
        var data = new Dictionary<string, string>()
        {
            {"chatId",chatId },
            { "body", text }
        };
        return await SendRequest("sendMessage", JsonConvert.SerializeObject(data));
    }

The method’s parameters are:

  •   chatId — the ID of the chat where the message is to be sent
  •   text — the text of the message to be sent

You can form the JSON string with the help of the handy Newtonsoft.Json library. Create a dictionary where, according to the documentation, the string with the corresponding JSON field will be the key and your parameters — the values. To do this, just call the JsonConvert.SerializeObject method, add your dictionary to it, and the JSON string will be formed. Then, call SendRequest with two parameters: the name of the method of sending messages and the JSON string.

So, this is how your bot will respond to users.

Sending Voice Messages

public async Task<string> SendOgg(string chatId)
    {
        string ogg = "https://firebasestorage.googleapis.com/v0/b/chat-api-com.appspot.com/o/audio_2019-02-02_00-50-42.ogg?alt=media&token=a563a0f7-116b-4606-9d7d-172426ede6d1";
        var data = new Dictionary<string, string>
        {
            {"audio", ogg },
            {"chatId", chatId }
        };

        return await SendRequest("sendAudio", JsonConvert.SerializeObject(data));
    }

The rest of the methods are built according to the same logic. You are supposed to look up the documentation and send the necessary data to the server by calling the corresponding methods.

To send a voice message, use the link to the .ogg file and the sendAudio method.

Method of sending the geolocation

public async Task<string> SendGeo(string chatId)
    {
        var data = new Dictionary<string, string>()
        {
            { "lat", "55.756693" },
            { "lng", "37.621578" },
            { "address", "Your address" },
            { "chatId", chatId}
        };
        return await SendRequest("sendLocation", JsonConvert.SerializeObject(data));
    }

Creating a Group

The method creates a conference for you and the bot.

public async Task<string> CreateGroup(string author)
    {
        var phone = author.Replace("@c.us", "");
        var data = new Dictionary<string, string>()
        {
            { "groupName", "Group C#"},
            { "phones", phone },
            { "messageText", "This is your group." }
        };
        return await SendRequest("group", JsonConvert.SerializeObject(data));
    }

The Method of Sending Files

The method uses such parameters as:

  •   chatId — the ID of the chat
  •   format — the format of the file to be sent.

According to the documentation, you can send files by either of the following ways:

  •   Providing a link to the file
  •   Providing a string containing the file encoded via the Base64 method

We recommend that you use the second method, that is, encoding files in the Base64 format. We will talk more about it in Chapter 4. For the moment though, we have created the static Base64String class with properties containing test files of all necessary formats. These properties are used to send test files to the server.

public async Task<string> SendFile(string chatId, string format)
    {
        var availableFormat = new Dictionary<string, string>()
        {
            {"doc", Base64String.Doc },
            {"gif",Base64String.Gif },

            { "jpg",Base64String.Jpg },
            { "png", Base64String.Png },
            { "pdf", Base64String.Pdf },
            { "mp4",Base64String.Mp4 },
            { "mp3", Base64String.Mp3}
        };

        if (availableFormat.ContainsKey(format))
        {
            var data = new Dictionary<string, string>(){
                { "chatId", chatId },
                { "body", availableFormat[format] },
                { "filename", "yourfile" },
                { "caption", $"My file!" }
            };

            return await SendRequest("sendFile", JsonConvert.SerializeObject(data));
        }

        return await SendMessage(chatId, "No file with this format");
    }

Now that we have covered the basic functionality of the API class, let’s move on and join the API with the controller from Chapter 1.

Chapter 3. Processing Requests

Coming back to the controller from Chapter 1, let’s create a method that will process post requests coming to the server from chat-api.com. Let’s name the method Post (you can also give it any other name) and mark it with the attribute [HttpPost] which means that it will react to Post requests.

[HttpPost]
public async Task<string> Post(Answer data)
    {
        return "";
    }

The method will accept the Answer class, i.e., the deserialized object we got from the JSON string. In order to implement the Answer class, we’ll have to know what JSON we will receive.

To do this, you can use the handy Testing - Webhook Simulation section in your user account.

The JSON body you will receive will show on the right.

You can also use the Conversion of JSON to C# service or create the class yourself by using the attributes of the Newtonsoft.Json library:

public partial class Answer
    {
        [JsonProperty("instanceId")]
        public string InstanceId { get; set; }

        [JsonProperty("messages")]
        public Message[] Messages { get; set; }
    }

public partial class Message
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("body")]
        public string Body { get; set; }

        [JsonProperty("type")]
        public string Type { get; set; }

        [JsonProperty("senderName")]
        public string SenderName { get; set; }

        [JsonProperty("fromMe")]
        public bool FromMe { get; set; }

        [JsonProperty("author")]
        public string Author { get; set; }

        [JsonProperty("time")]
        public long Time { get; set; }

        [JsonProperty("chatId")]
        public string chatId { get; set; }

        [JsonProperty("messageNumber")]
        public long MessageNumber { get; set; }
    }

Now that you have an object representation of the incoming request, it’s time to process it in the controller.

Inside the controller, create a static field which will serve as both an API link and a token.

private static readonly WaApi api = new WaApi("https://eu115.chat-api.com/instance12345/", "123456789token");

In the method’s cycle, process all the incoming messages checking if they are not your own. In this way, you will make sure your bot is not looped on itself. If you see a message from yourself, just skip it:

[HttpPost]
public async Task<string> Post(Answer data)
    {
        foreach (var message in data.Messages)
        {
            if (message.FromMe)
                continue;
        }
    }

Next, you need to get the command from the incoming message. To do this, extract the first word from message.Body using Split() and convert it to the lower case using toLower(). And, finally, process the command using the switch statement.

[HttpPost]
public async Task<string> Post(Answer data)
    {
        foreach (var message in data.Messages)
        {
            if (message.FromMe)
                continue;

            switch (message.Body.Split()[0].ToLower())
            {
                case "chatId":
                    return await api.SendMessage(message.chatId, $"Your ID: {message.chatId}");
                case "file":
                    var texts = message.Body.Split();
                    if (texts.Length > 1)
                        return await api.SendFile(message.chatId, texts[1]);
                    break;
                case "ogg":
                    return await api.SendOgg(message.chatId);
                case "geo":
                    return await api.SendGeo(message.chatId);
                case "group":
                    return await api.CreateGroup(message.Author);
                default:
                    return await api.SendMessage(message.chatId, welcomeMessage);
            }
        }
        return "";
    }

Write into the case all the commands you need and call methods that realize them from your API’s object. As for default, it will process commands that don’t exist and send messages from the bot’s menu to users.

WhatsApp Bot on Csharp

Detailed guide to bot development using Csharp

Your bot is ready! It can process and respond to user commands. All that is left for you to do now is add it to the hosting and specify your domain as a webhook in your chat-api.com user account.

The source code of the bot will be available at GitHub via the link: https://github.com/chatapi/whatsapp-csharp-bot-en. Don’t forget to insert your token and instance number from your user account.

Get API key

Chapter 4. Base64

Base64 is a standard for encoding data. It allows for converting files to strings and sending them as such.

In your user account, you can find the service that helps generate strings of this format. When writing the bot, we had to declare some static properties for testing which is why we inserted the received strings into the utility class and called them from the code. However, you may prefer to encode files “on the go”.

You can also generate strings like these by using built-in C# tools.

Chapter 5. Server Publishing

To install a server as a webhook, you need to upload the server to the Internet. To do this, you can use services that offer hosting, VPS, or VDS servers. You will need to choose and pay for the service that supports the ASP.Net technology.

Problems you May Encounter on your Way

  •   You can’t connect to the hosting server and publish your server. Solution: Contact Technical Support and ask them to enable Web Deploy for you.

AJAX Gridview CRUD Operations (Insert, Read, Update, Delete) in ASP.Net with Updatepanel

 Here I will explain how to implement ajax gridview crud operations insert, select, edit, update and delete with single stored procedure in asp.net using c#, vb.net with example or insert, update, delete operations (crud) in asp.net gridview without postback using updatepanel with single stored procedure in c#, vb.net with example.


Description:
  
In previous articles I explained 
gridview examples in asp.netdisplay images from database using handler in asp.netBind Dropdownlist selected value in asp.net gridviewgridview rowdatabound event example in asp.netDelete multiple rows in gridview using checkbox in asp.net and many articles relating to gridviewasp.netc#,vb.net and jQuery. Now I will explain how to implement ajax gridview crud operations (insert, select, edit, update) in asp.net with single stored procedure using c#, vb.net with example.

Before implement this example first design one table productinfo in your database as shown below

Column Name
Data Type
Allow Nulls
productid
Int(IDENTITY=TRUE)
Yes
productname
varchar(50)
Yes
price
varchar(50)
Yes
Now create one new stored procedure “Crudoperations” in your sql server database to perform insert, select, update and delete operations with single procedure for that follow below script


CREATE PROCEDURE CrudOperations 
@productid int = 0, 
@productname varchar(50)=null, 
@price int=0, 
@status varchar(50) 
AS 
BEGIN 
SET NOCOUNT ON; 
--- Insert New Records 
IF @status='INSERT' 
BEGIN 
INSERT INTO productinfo1(productname,price) VALUES(@productname,@price) 
END 
--- Select Records in Table 
IF @status='SELECT' 
BEGIN 
SELECT productid,productname,price FROM productinfo1 
END 
--- Update Records in Table  
IF @status='UPDATE' 
BEGIN 
UPDATE productinfo1 SET productname=@productname,price=@price WHERE productid=@productid 
END 
--- Delete Records from Table 
IF @status='DELETE' 
BEGIN 
DELETE FROM productinfo1 where productid=@productid 
END 
SET NOCOUNT OFF 
END

In case if you have any doubts to create procedure check below article



Once we finish stored procedure creation in database now open your aspx page and write the code like as shown below


<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Ajax GridView CRUD: Select Insert Edit Update Delete using Single Stored Procedure in ASP.Net</title>
<style type="text/css">
.GridviewDiv {font-size100%font-family'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helevetica, sans-serifcolor#303933;}
.headerstyle
{
color:#FFFFFF;border-right-color:#abb079;border-bottom-color:#abb079;background-color#df5015;padding:0.5em 0.5em 0.5em 0.5em;text-align:center;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="scriptmanager1" runat="server"></asp:ScriptManager>
<div class="GridviewDiv">
<asp:UpdatePanel ID="panel1" runat="server">
<ContentTemplate>
<asp:GridView runat="server" ID="gvDetails" ShowFooter="true" AllowPaging="true" PageSize="10" AutoGenerateColumns="false" DataKeyNames="productid,productname" OnPageIndexChanging="gvDetails_PageIndexChanging" OnRowCancelingEdit="gvDetails_RowCancelingEdit"
OnRowEditing="gvDetails_RowEditing" OnRowUpdating="gvDetails_RowUpdating" OnRowDeleting="gvDetails_RowDeleting" OnRowCommand ="gvDetails_RowCommand" >
<HeaderStyle CssClass="headerstyle" />
<Columns>
<asp:BoundField DataField="productid" HeaderText="Product Id" ReadOnly="true" />
<asp:TemplateField HeaderText="Product Name">
<ItemTemplate>
<asp:Label ID="lblProductname" runat="server" Text='<%# Eval("productname")%>'/>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtProductname" runat="server" Text='<%# Eval("productname")%>'/>
</EditItemTemplate>
<FooterTemplate>
<asp:TextBox ID="txtpname" runat="server" />
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText = "Price">
<ItemTemplate>
<asp:Label ID="lblPrice" runat="server" Text='<%# Eval("price")%>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtProductprice" runat="server" Text='<%# Eval("price")%>'/>
</EditItemTemplate>
<FooterTemplate>
<asp:TextBox ID="txtprice" runat="server" />
<asp:Button ID="btnAdd" CommandName="AddNew" runat="server" Text="Add" />
</FooterTemplate>
</asp:TemplateField>
<asp:CommandField ShowEditButton="True" ShowDeleteButton="true" />
</Columns>
</asp:GridView>
<asp:Label ID="lblresult" runat="server"></asp:Label>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="gvDetails" />
</Triggers>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
After completion of aspx page add following namespaces in codebehind

C# Code


using System;
using System.Web.UI.WebControls;
using System.Data.SqlClient;
using System.Data;
using System.Drawing;

After completion of adding namespaces you need to write the code like as shown below


protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindGridview();
}
}
protected void BindGridview()
{
DataSet ds = new DataSet();
using (SqlConnection con = new SqlConnection("Data Source=Suresh;Integrated Security=true;Initial Catalog=MySampleDB"))
{
con.Open();
SqlCommand cmd = new SqlCommand("crudoperations", con);
cmd.CommandType= CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@status","SELECT");
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds);
con.Close();
if (ds.Tables[0].Rows.Count > 0)
{
gvDetails.DataSource = ds;
gvDetails.DataBind();
}
else {
ds.Tables[0].Rows.Add(ds.Tables[0].NewRow());
gvDetails.DataSource = ds;
gvDetails.DataBind();
int columncount = gvDetails.Rows[0].Cells.Count;
gvDetails.Rows[0].Cells.Clear();
gvDetails.Rows[0].Cells.Add(new TableCell());
gvDetails.Rows[0].Cells[0].ColumnSpan = columncount;
gvDetails.Rows[0].Cells[0].Text = "No Records Found";
}
}
}
protected void gvDetails_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName.Equals("AddNew"))
{
TextBox txtname = (TextBox)gvDetails.FooterRow.FindControl("txtpname");
TextBox txtprice = (TextBox)gvDetails.FooterRow.FindControl("txtprice");
crudoperations("INSERT", txtname.Text, txtprice.Text, 0);
}
}
protected void gvDetails_RowEditing(object sender, GridViewEditEventArgs e)
{
gvDetails.EditIndex = e.NewEditIndex;
BindGridview();
}
protected void gvDetails_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
gvDetails.EditIndex = -1;
BindGridview();
}
protected void gvDetails_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
gvDetails.PageIndex = e.NewPageIndex;
BindGridview();
}
protected void gvDetails_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
int productid = Convert.ToInt32(gvDetails.DataKeys[e.RowIndex].Values["productid"].ToString());
TextBox txtname = (TextBox)gvDetails.Rows[e.RowIndex].FindControl("txtProductname");
TextBox txtprice = (TextBox)gvDetails.Rows[e.RowIndex].FindControl("txtProductprice");
crudoperations("UPDATE",txtname.Text,txtprice.Text,productid);
}
protected void gvDetails_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
int productid = Convert.ToInt32(gvDetails.DataKeys[e.RowIndex].Values["productid"].ToString());
string productname = gvDetails.DataKeys[e.RowIndex].Values["productname"].ToString();
crudoperations("DELETE",productname,"",productid);
}
protected void crudoperations(string status, string productname, string price, int productid)
{
using (SqlConnection con = new SqlConnection("Data Source=Suresh;Integrated Security=true;Initial Catalog=MySampleDB"))
{
con.Open();
SqlCommand cmd = new SqlCommand("crudoperations", con);
cmd.CommandType= CommandType.StoredProcedure;
if(status=="INSERT")
{
cmd.Parameters.AddWithValue("@status",status);
cmd.Parameters.AddWithValue("@productname",productname);
cmd.Parameters.AddWithValue("@price",price);
}
else if(status=="UPDATE")
{
cmd.Parameters.AddWithValue("@status",status);
cmd.Parameters.AddWithValue("@productname",productname);
cmd.Parameters.AddWithValue("@price",price);
cmd.Parameters.AddWithValue("@productid",productid);
}
else if(status=="DELETE")
{
cmd.Parameters.AddWithValue("@status",status);
cmd.Parameters.AddWithValue("@productid",productid);
}
cmd.ExecuteNonQuery();
lblresult.ForeColor = Color.Green;
lblresult.Text = productname+" details "+status.ToLower()+"d successfully";
gvDetails.EditIndex = -1;
BindGridview();
}
}

VB.NET Code


Imports System.Web.UI.WebControls
Imports System.Data.SqlClient
Imports System.Data
Imports System.Drawing

Partial Class VBCode
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As ObjectByVal e As EventArgsHandles Me.Load
If Not IsPostBack Then
BindGridview()
End If
End Sub
Protected Sub BindGridview()
Dim ds As New DataSet()
Using con As New SqlConnection("Data Source=Suresh;Integrated Security=true;Initial Catalog=MySampleDB")
con.Open()
Dim cmd As New SqlCommand("crudoperations", con)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("@status""SELECT")
Dim da As New SqlDataAdapter(cmd)
da.Fill(ds)
con.Close()
If ds.Tables(0).Rows.Count > 0 Then
gvDetails.DataSource = ds
gvDetails.DataBind()
Else
ds.Tables(0).Rows.Add(ds.Tables(0).NewRow())
gvDetails.DataSource = ds
gvDetails.DataBind()
Dim columncount As Integer = gvDetails.Rows(0).Cells.Count
gvDetails.Rows(0).Cells.Clear()
gvDetails.Rows(0).Cells.Add(New TableCell())
gvDetails.Rows(0).Cells(0).ColumnSpan = columncount
gvDetails.Rows(0).Cells(0).Text = "No Records Found"
End If
End Using
End Sub
Protected Sub gvDetails_RowCommand(ByVal sender As ObjectByVal e As GridViewCommandEventArgs)
If e.CommandName.Equals("AddNew"Then
Dim txtname As TextBox = DirectCast(gvDetails.FooterRow.FindControl("txtpname"), TextBox)
Dim txtprice As TextBox = DirectCast(gvDetails.FooterRow.FindControl("txtprice"), TextBox)
crudoperations("INSERT", txtname.Text, txtprice.Text, 0)
End If
End Sub
Protected Sub gvDetails_RowEditing(ByVal sender As ObjectByVal e As GridViewEditEventArgs)
gvDetails.EditIndex = e.NewEditIndex
BindGridview()
End Sub
Protected Sub gvDetails_RowCancelingEdit(ByVal sender As ObjectByVal e As GridViewCancelEditEventArgs)
gvDetails.EditIndex = -1
BindGridview()
End Sub
Protected Sub gvDetails_PageIndexChanging(ByVal sender As ObjectByVal e As GridViewPageEventArgs)
gvDetails.PageIndex = e.NewPageIndex
BindGridview()
End Sub
Protected Sub gvDetails_RowUpdating(ByVal sender As ObjectByVal e As GridViewUpdateEventArgs)
Dim productid As Integer = Convert.ToInt32(gvDetails.DataKeys(e.RowIndex).Values("productid").ToString())
Dim txtname As TextBox = DirectCast(gvDetails.Rows(e.RowIndex).FindControl("txtProductname"), TextBox)
Dim txtprice As TextBox = DirectCast(gvDetails.Rows(e.RowIndex).FindControl("txtProductprice"), TextBox)
crudoperations("UPDATE", txtname.Text, txtprice.Text, productid)
End Sub
Protected Sub gvDetails_RowDeleting(ByVal sender As ObjectByVal e As GridViewDeleteEventArgs)
Dim productid As Integer = Convert.ToInt32(gvDetails.DataKeys(e.RowIndex).Values("productid").ToString())
Dim productname As String = gvDetails.DataKeys(e.RowIndex).Values("productname").ToString()
crudoperations("DELETE", productname, "", productid)
End Sub
Protected Sub crudoperations(ByVal status As StringByVal productname As StringByVal price As StringByVal productid As Integer)
Using con As New SqlConnection("Data Source=Suresh;Integrated Security=true;Initial Catalog=MySampleDB")
con.Open()
Dim cmd As New SqlCommand("crudoperations", con)
cmd.CommandType = CommandType.StoredProcedure
If status = "INSERT" Then
cmd.Parameters.AddWithValue("@status", status)
cmd.Parameters.AddWithValue("@productname", productname)
cmd.Parameters.AddWithValue("@price", price)
ElseIf status = "UPDATE" Then
cmd.Parameters.AddWithValue("@status", status)
cmd.Parameters.AddWithValue("@productname", productname)
cmd.Parameters.AddWithValue("@price", price)
cmd.Parameters.AddWithValue("@productid", productid)
ElseIf status = "DELETE" Then
cmd.Parameters.AddWithValue("@status", status)
cmd.Parameters.AddWithValue("@productid", productid)
End If
cmd.ExecuteNonQuery()
lblresult.ForeColor = Color.Green
lblresult.Text = (productname & Convert.ToString(" details ")) + status.ToLower() + "d successfully"
gvDetails.EditIndex = -1
BindGridview()
End Using
End Sub
End Class

Demo

AJAX CRUD operations (Create, Read, Update and Delete) using GridView in ASP.Net


How to register multiple implementations of the same interface in Asp.Net Core?

 Problem: I have services that are derived from the same interface. public interface IService { } public class ServiceA : IService { ...