Geek Culture
Published in

Geek Culture

Bitwise Implementation on .Net 6

Hi, today we will talk about the advantages and disadvantages of Bitwise Operators and Operations in a real-life scenario.

Scenario: We will send a campaign message to Dealers. And there are lots of users under these dealers. We will try to record who read the message and who did not.

Classic Way

These are MsSqlDB tables: “Dealer”, “Message” relation table “UserMessage” and of course “User” table.

BitwiseDB’s Tables Schema

In a classic way, we kept who read the message data in the “UserMessage” table, which is surrounded by the green rectangle as seen below.

1-) Let’s create .Net 6.0 Web Api Project “BitwiseDealers”. And Add DB ClassLibrary Project to the solution.

Visual Studio 2022 .Net Project Template

Add below Libraries by using Nuget to the DB Project.

Create Entity and DBContext

DB already exists so we will use DB First for this solution. Open Terminal and move into the DB Folder with “cd DB” command. We call below “scaffold” command. So we will create Entity and BitwiseContext automatically.

This is new DB Layer

BitwiseDealers/appsettings.json: We added DB connection settings to appsettings.json for the WebApi service.

*Don’t forget to encrypt all config text for the release version.

2-) Create Service Layer: Create “BitwiseService” ClassLibrary Project.

BitwiseService/IUserService: We will get the User list of, who read the specific message.

BitwiseService/UserService: We will return custom “UserMessageViewModel” from this “GetUserListByMessageID()” method. We will get User FullName, DealerName and ReadStatus. And return it to the Controller.

Program.cs: Don’t forget to add userService to program.cs as below.

3-) Create Core Class Library Project and add Model Folder.

Core/Model/UserMessageViewModel: This is custom UserMessageViewModel

4-) BitwiseDealers/Controllers/DealerController: We will get UserList from BitwiseDealers Controller.

In this way, we have to keep all user’s records one by one for every Dealers. If you have 10K users, for every message you have to repeat this. So at the end of the month, you have to keep millions of rows for every message. This is unacceptable to me :)

How to keep the same data with much fewer rows for optimization and performance ?

Bitwise Way

1-) Firstly, we will add “BitwiseID” column to User table. And we will create a new “UserMessageBitwase” table as seen below. After all, we will use the “UserMessageBitwise” table instead of the “UserMessage

*Don’t forget to call the “scaffold” command again. Because of Update the Backend DBContext and Entities. We will update User Entitiy and add UserMessageBitwise table.

The user’s BitwiseID will be increased +1 with the “2^n” formula.

Example: “1, 2, 4, 8, 16”. We will set “BitwiseID” per user group by DealerID. If DealerID changes, we will start BitwiseID from 1 again as seen below.

We will add new data to “UserMessageBitwise” table.

The trick is we will sum the User’s “BitWiseID”, who read the message in the same group with DealarID.

For example : If you check the “UserMessage Table” for MessageID =1 and in a DealerID =1, group users are “1,2 and 4"

We will write only 1 row to the UserMessageBitwise Table instead of writing 3 rows to the UserMessage Table

We will sum the User’s “BitWiseID” whose DealerID=1 and User Id = 1,2 and 4 as seen below. It is 1 + 2 + 8= “11” and will put this total number to the “TotalBitwiseId” column of “UserMessageBitwise” table as seen above. So we will write 1 row instead of 3 rows.

Sum Spesific User’s BitwiseID

2-)Let’s change the Backend “GetUserListByMessageID()” by Using BitwiseID

Firstly we need Redis for getting all User “BitwiseId” from memory for every Dealer answer data to improve performance. Our main goal is to increase performance by transferring the load on SQL to Redise.

Create “Caching” folder under “Core” folder. And add “RedisCacheService” as seen below.

Core/Caching/IRedisCacheService:

Don’t Forget the Download “ServiceStack.Redis” Library for Core Project

BitwiseDealers/appsettings.json: Add Redis config to appsetting.json. Don’t forget to encrypt all config text for the release version.

Core/Configuration/BitwiseConfig: We will use BitwiseConfig class, for getting configuration from appsettings.json.

BitwiseDealer/Program.cs: Don’t forget to add “BitwiseConfig” class to program.cs as seen below. So with this, we will match the “appsettings.json/BitwiseConfig” section to the BitwiseConfig class.

Core/Caching/RedisCacheService: This is a basic simple RedisCache service. You can Add, Get and Remove base operations by using this class.

BitwiseDealer/Program.cs: Don’t forget to add “RedisServices” class to program.cs as seen below.

3-)Next, We will create “UserRedisModel” as a model which is kept in Redis. We will keep user Detail data in the Redis with this model.

List of UserRedisModel Data for DealarID=1 on Redis

Core/Model/UserRedisModel:

We made all Setup. Next, we will change GetUserListMessageID() method with the Bitwise algorithm as seen below

  • In the beginning, we will get all who read the message data and set the “answerData” parameter.
  • We will get the message text by “messageID” and set the message variable.
  • “resultList” is our return List of the model. We will create an empty model ad the beginning. And we will start to loop in “answerData”.
  • We will try to get user data with a specific dealerID from the Redis. If it is null will get all data from SqlDB.
  • If Redis is null, we will get user data from DB with LINQ as seen below. We will join the User and Dealer table and return UserRedisModel. And finally, save this dealer’s user data to the Redis.
  • *This is the most important part. After we got user data from the Redis or SqlDB, we will loop in UserList. We will check if “User.BitwiseID” is in the total answered “TotalUserBitwiseId”. It means, is this user read this message or not.

user.BitwiseID[4] = (TotalBitwiseID[8] & user.BitwiseID[4]) => result is “Yes”

Bitwise Operation Example
  • If it is true, we will add this user in to the “resultUserList”. And finally return it.
We Checked if Is User Read the Message or Not by Using Bitwise

BitwiseService/UserService (Version 2):

We got all the users, who read the MessageID=1 by using “Bitwiseas seen below.

Which Users Did Read the MessageID =1 ?

Conclusion:

You can use Bitwise Algorithms for lots of parent-child scenarios. For example Tree Menu, State-City or Product-SubProduct. You can use bitwise for Max 63 subitems. Because the processor can not recognize bigger than “2^63" numbers. But I think 63 is enough for most cases. You can write online 1 line instead of 63 lines. The trick is to use subcategories with a group. As in this article, we grouped users under the Dealer. So for different Dealer, we started User BitwiseID from 1 again. And we earned one more 63 users for the new Dealer.

Don’t worry about looping all users to check whether their’s BitwiseId is in the TotalBitwiseID or not. Because checking bitwise is a very performance operation.

See you until the next article.

“If you have read so far, first of all, thank you for your patience and support. I welcome all of you to my blog for more!”

Source Code

--

--

A new tech publication by Start it up (https://medium.com/swlh).

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Bora Kaşmer

I have been coding since 1993. I am computer and civil engineer. Microsoft MVP. Senior Software Architect. Ride motorcycle. Gamer. Have two daughters.