asp.net - C# ASP .NET: How to transform code to more concurrent? -


i'm making auction web application using c# asp .net.

my web application got 2 methods must synchronize, can't let them invoke @ same time, because make conflicts in database, i've used lock(object) code blocks protect these 2 methods.

i need make more concurrent, because solution locking full methods makes application slow @ moments.

i've used signalr library send notifications between server , clients, , myregistry class make timer ticks every second , send notifications online clients.

in opinion lines of code access database, , signalr methods sending notifications lines maybe slows system.

here's object that's used lock object:

public static class mutex {     public static string lockobject = "mutex"; } 

my hub (signalr)

public class myhub : hub {      private static dictionary<string, string> hashusersconnids = new dictionary<string, string>(512);     private iep_model db = new iep_model();     public readonly log4net.ilog logger = log4net.logmanager.getlogger(system.reflection.methodbase.getcurrentmethod().declaringtype);      // server hub     public void send(long idauc, string lastbidderemail)     {          aspnetuser user = db.aspnetusers.singleordefault(r => r.email == lastbidderemail);          lock (mutex.lockobject)         {              auction auction = db.auctions.singleordefault(a => a.idauc == idauc);             long productnewprice = auction.price + auction.increment + 1;              // user can bid!             if ((productnewprice <= user.tokennumber))             {                 long newbiddercount = user.tokennumber - productnewprice;                 user.tokennumber = newbiddercount;                 db.entry(user).state = entitystate.modified;                  bid newbid = new bid();                 newbid.id = user.id;                 newbid.idauc = auction.idauc;                 newbid.bidtime = datetime.now;                 newbid.tokens = productnewprice;                 db.bids.add(newbid);                  // return tokens previous client                 var previousbidder =    o                                         in db.userauctioninvested                                         o.auctionid == auction.idauc                                         select o.userid;                  string previousbidderid = previousbidder.singleordefault();                 aspnetuser previous = db.aspnetusers.find(previousbidderid);                  // previous bidder exists                 if (previousbidderid != null)                 {                     long tokenstoreturn = (long)db.userauctioninvested.where(u => u.auctionid == auction.idauc && u.userid == previousbidderid).singleordefault().tokeninvested;                     long newtokencount = previous.tokennumber + tokenstoreturn;                     previous.tokennumber = newtokencount;                      // push notification: set previous client's token number                     var clientselector = "token" + previous.email.replace("@", "_");                     var clientalertselector = "alerttoken" + previous.email.replace("@", "_");                     var warningalertselector = "warning" + previous.email.replace("@", "_");                     // previous client online                     if (hashusersconnids.containskey(previous.email) && previous.email != lastbidderemail)                     {                         clients.client(hashusersconnids[previous.email]).settokennumber(clientselector, newtokencount, clientalertselector, warningalertselector, auction.product_name);                     }                     // push notification: current client (last bidder) new token count                     clientselector = "token" + lastbidderemail.replace("@", "_");                     clientalertselector = "alerttoken" + lastbidderemail.replace("@", "_");                     warningalertselector = "warning" + previous.email.replace("@", "_");                     if (previous.email != lastbidderemail && hashusersconnids.containskey(lastbidderemail)) {                         clients.client(hashusersconnids[lastbidderemail]).settokennumber(clientselector, newbiddercount, clientalertselector, "x", "x");                     }                     db.entry(previous).state = entitystate.modified;                     userauctioninvested uaiprevious = db.userauctioninvested.find(previousbidderid, auction.idauc);                     db.userauctioninvested.remove(uaiprevious);                 }                  // client first bidder                 else                 {                     var clientselector = "token" + lastbidderemail.replace("@", "_");                     var clientalertselector = "alerttoken" + lastbidderemail.replace("@", "_");                     // push notification: current client (last bidder) new token count                     if (hashusersconnids.containskey(lastbidderemail))                     {                         clients.client(hashusersconnids[lastbidderemail]).settokennumber(clientselector, newbiddercount, clientalertselector, "x", "x");                     }                 }                  // creating new bid userauctioninvested -> [dbo].[userauctioninvested] (user, auction, tokenno)                 userauctioninvested uai = new userauctioninvested();                 uai.auctionid = auction.idauc;                 uai.userid = user.id;                 uai.tokeninvested = productnewprice;                 db.userauctioninvested.add(uai);                  // update auction                 auction.increment += 1;                 auction.lastbidder = lastbidderemail;                  // seconds end of auction                 var secondsdifference = ((datetime)auction.close_date_time - datetime.now).totalseconds;                 if (secondsdifference <= 10)                 {                     datetime oldclosedatetime = (datetime)auction.close_date_time;                     datetime newclosedatetime = oldclosedatetime.addseconds(10);                     auction.close_date_time = newclosedatetime;                     auction.duration += 10;                 }                  db.entry(auction).state = entitystate.modified;                 string remainingtoend = ((datetime)auction.close_date_time - datetime.now).tostring(@"dd\:hh\:mm\:ss");                 clients.all.clientbidsupdate(idauc, auction.state, remainingtoend, lastbidderemail, auction.price + auction.increment, "false");                 // update details auction page // clientwarningselector, auctionnamewarning                 clients.all.auctiondetailsupdate(idauc, lastbidderemail, auction.price + auction.increment, newbid.bidtime.tostring(@"dd\:hh\:mm\:ss"), "open");                 db.savechanges();                 return;              }             // client previous bidder needs pay +1 on actual price             else if (auction.lastbidder == user.email)             {                 if (user.tokennumber > 0) // can place next bid                 {                     user.tokennumber = user.tokennumber - 1;                     db.entry(user).state = entitystate.modified;                     bid newbid = new bid();                     newbid.id = user.id;                     newbid.idauc = auction.idauc;                     newbid.bidtime = datetime.now;                     newbid.tokens = auction.price + auction.increment + 1;                     db.bids.add(newbid);                     if (hashusersconnids.containskey(lastbidderemail))                     {                         var clientselector = "token" + lastbidderemail.replace("@", "_");                         var clientalertselector = "alerttoken" + lastbidderemail.replace("@", "_");                         var clientwarningselector = "warning" + lastbidderemail.replace("@", "_");                         clients.client(hashusersconnids[lastbidderemail]).settokennumber(clientselector, user.tokennumber, clientalertselector, "x", "x");                     }                     // updating userauctioninvested -> [dbo].[userauctioninvested] (user, auction, tokenno)                     userauctioninvested uai = db.userauctioninvested.where(u => u.auctionid == auction.idauc && u.userid == user.id).singleordefault();                     uai.tokeninvested += 1;                     db.entry(uai).state = entitystate.modified;                     // update auction                     auction.increment += 1;                     // seconds end of auction                     var secondsdifference = ((datetime)auction.close_date_time - datetime.now).totalseconds;                     if (secondsdifference <= 10)                     {                         datetime oldclosedatetime = (datetime)auction.close_date_time;                         datetime newclosedatetime = oldclosedatetime.addseconds(10);                         auction.close_date_time = newclosedatetime;                         auction.duration += 10;                     }                     db.entry(auction).state = entitystate.modified;                     string remainingtoend = ((datetime)auction.close_date_time - datetime.now).tostring(@"dd\:hh\:mm\:ss");                     clients.all.clientbidsupdate(idauc, auction.state, remainingtoend, lastbidderemail, auction.price + auction.increment, "false");                     clients.all.auctiondetailsupdate(idauc, lastbidderemail, auction.price + auction.increment, newbid.bidtime.tostring(@"dd\:hh\:mm\:ss"), "open");                      db.savechanges();                     return;                 }             }             // no tokens - warn user!             string remaining = ((datetime)auction.close_date_time - datetime.now).tostring(@"dd\:hh\:mm\:ss");             clients.all.clientbidsupdate(idauc, auction.state, remaining, lastbidderemail, auction.price + auction.increment, "true");         }     }      // registring client     public void registerconid(string email)     {         hashusersconnids[email] = context.connectionid;     }  } 

and here's registry class acts timer on web application, ticks every 1 seconds , updates database:

public class myregistry : registry {      public readonly log4net.ilog logger = log4net.logmanager.getlogger(system.reflection.methodbase.getcurrentmethod().declaringtype);      public myregistry()     {          schedule(() =>         {              // check if auction on each 1 sec             lock (mutex.lockobject)             {                  iep_model db = new iep_model();                 var hubcontext = globalhost.connectionmanager.gethubcontext<myhub>();                  var auctions = o                                 in db.auctions                                o.state == "open"                                select o;                  var auctionslist = auctions.tolist();                  foreach (var auction in auctionslist)                 {                      datetime = datetime.now;                     datetime end = (datetime)auction.close_date_time;                      // time up! auction "expired" or "sold"                     if (now >= end)                     {                         var edited = db.auctions.find(auction.idauc);                         // no winner                         if (edited.increment == 0)                         {                             edited.state = "expired";                             db.entry(edited).state = entitystate.modified;                             timespan timeend = new timespan(0);                             string newdurationexpired = timeend.tostring(@"dd\:hh\:mm\:ss");                              hubcontext.clients.all.timerupdate(auction.idauc, edited.state, newdurationexpired, " - ", edited.price, "false", "odd", "true");                             db.savechanges();                         }                         // yes winner                         else                         {                             edited.state = "sold";                             db.entry(edited).state = entitystate.modified;                             db.savechanges();                              // refresh client                             long soldprice = edited.price + edited.increment;                             timespan timeend = new timespan(0);                             string newdurationsold = timeend.tostring(@"dd\:hh\:mm\:ss");                             hubcontext.clients.all.timerupdate(auction.idauc, edited.state, newdurationsold, edited.lastbidder, soldprice, "false", "odd", "true");                              // return tokens non-winners                             string winnerid = db.aspnetusers.where(a => a.email == edited.lastbidder).singleordefault().id;                             //var bids = db.bids.where(b => b.idauc == auction.idauc && b.id != winnerid).tolist();                             var userinvested = db.userauctioninvested.where(i => i.auctionid == auction.idauc);                              foreach (var item in userinvested.tolist())                             {                                 if (item.userid != winnerid) // not winner - return money                                 {                                     aspnetuser fetchuser = db.aspnetusers.find(item.userid);                                     fetchuser.tokennumber += (long)item.tokeninvested;                                     db.entry(fetchuser).state = entitystate.modified;                                 }                                 db.userauctioninvested.remove(item);                             }                              db.savechanges();                          }                     }                      // update auction - still active                     long actualprice = auction.price + auction.increment;                     timespan difference = end.subtract(now);                     string newduration = difference.tostring(@"dd\:hh\:mm\:ss");                      string lasttenseconds__ = "false";                     string numberoddeven__ = "odd";                     string end__ = "false";                     if (difference.totalseconds <= 10)                     {                         lasttenseconds__ = "true";                         if (difference.totalseconds <= 1)                         {                             end__ = "true";                         }                         if ((int)math.ceiling(difference.totalseconds) % 2 == 0)                          {                             numberoddeven__ = "even";                         }                     }                     hubcontext.clients.all.timerupdate(auction.idauc, auction.state, newduration, auction.lastbidder, actualprice, lasttenseconds__, numberoddeven__, end__);                   }              }         }).torunnow().andevery(1).seconds();     }  } 

i'd need idea how transform bit code, make more concurrent. i've seen c# supports monitors, wondering if benefit using it?

looking @ code, first thing suggest reducing db calls few possible, front load current auctions, logged in users, , else static, , either cache them using redis (in memory caching tool) or in dictionaries. (for binary search performance)

secondly suggest multi-threaded process uses concurrentdictionary live auction tracking, has speed looking for, , can wrap on blockingcollection if need thread wait changes. way not have use locks. can off load persisting data storage using queue.

if have pluralsight suggest course:

https://app.pluralsight.com/library/courses/csharp-concurrent-collections/table-of-contents


Comments