IN THIS ARTICLE
You can easily build a live realtime voting app in the browser with C# and .NET, enabling users to vote and see results, updated in realtime.
Have you ever thought of implementing the C# PubNub API with ASP.NET MCV4? Whether you have or not, in this tutorial, we’ll walk you through how to build a web-based realtime voting application. This will enable users to vote, and the results will be automatically tallied and displayed. We’ll be using the PubNub C# SDK for both publish()
and DetailedHistory
to demo the voting web app.
In a realtime application, we expect the question and the choice of answers/responses come from a database. To keep things simple, I’ll hard code the questions and answer options as a xml string in GetActivePollQuestion()
method of SampleData
class. In real-time, you can return dynamic data as xml string.
Check out our simple realtime voting app below to get a better idea of what you’ll be building. Or feel free to take a look at the realtime voting GitHub Repo:
Schemas for a Realtime Voting App
Two schemas PollQuestion.xsd and PollUserAnswer.xsd were created.
PollQuestion.cs and PollUserAnswer.cs class files were generated using xsd.exe tool using the following commands:
XSD.EXE PollQuestion.xsd /c /l:cs /n:ePoll.Types | |
XSD.EXE PollUserAnswer.xsd /c /l:cs /n:ePoll.Types |
Take a look at their schemas below:
<xs:schema id="PollQuestion" | |
targetNamespace="pubnub-csharp-demo-epoll-1.0" | |
elementFormDefault="qualified" | |
xmlns="pubnub-csharp-demo-epoll-1.0" | |
xmlns:mstns="pubnub-csharp-demo-epoll-1.0" | |
xmlns:xs="http://www.w3.org/2001/XMLSchema"> | |
<xs:element name="PollQuestion"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element name="ID" type="xs:string"></xs:element> | |
<xs:element name="Question" type="xs:string"></xs:element> | |
<xs:element name="Active" type="xs:boolean"></xs:element> | |
<xs:element name="AnswerGroup"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element name="Answer" type="xs:string" maxOccurs="unbounded" minOccurs="2"></xs:element> | |
</xs:sequence> | |
</xs:complexType> | |
</xs:element> | |
</xs:sequence> | |
</xs:complexType> | |
</xs:element> | |
</xs:schema> |
<xs:schema id="PollUserAnswer" | |
targetNamespace="pubnub-csharp-demo-epoll-1.0" | |
elementFormDefault="qualified" | |
xmlns="pubnub-csharp-demo-epoll-1.0" | |
xmlns:mstns="pubnub-csharp-demo-epoll-1.0" | |
xmlns:xs="http://www.w3.org/2001/XMLSchema"> | |
<xs:element name="PollUserAnswer"> | |
<xs:complexType> | |
<xs:sequence> | |
<xs:element name="QuestionID" type="xs:string"></xs:element> | |
<xs:element name="Question" type="xs:string"></xs:element> | |
<xs:element name="UserAnswer" type="xs:string"></xs:element> | |
</xs:sequence> | |
</xs:complexType> | |
</xs:element> | |
</xs:schema> |
Publishing Messages over WebSockets for Realtime Voting
The Publish<string>()
method is being used to save the poll answers. The following code saves the answers.
public bool SaveAnswer(PollUserAnswer answer) | |
{ | |
bool ret = false; | |
string pubnubChannel = answer.QuestionID; | |
mrePublish.AddOrUpdate(pubnubChannel, new ManualResetEvent(false), (key, oldState) => new ManualResetEvent(false)); | |
messagePublished[pubnubChannel] = false; | |
pubnub.Publish<string>(pubnubChannel, answer.UserAnswer, PollUserAnswerPublishRegularCallback, PollUserAnswerPublishErrorCallback); | |
mrePublish[pubnubChannel].WaitOne(TimeSpan.FromSeconds(10)); | |
if (messagePublished[pubnubChannel]) | |
{ | |
ret = true; | |
} | |
return ret; | |
} | |
private static void PollUserAnswerPublishRegularCallback(string publishResult) | |
{ | |
if (!string.IsNullOrEmpty(publishResult) && !string.IsNullOrEmpty(publishResult.Trim())) | |
{ | |
object[] deserializedMessage = pubnub.JsonPluggableLibrary.DeserializeToObject(publishResult) as object[]; | |
if (deserializedMessage is object[] && deserializedMessage.Length == 3) | |
{ | |
long statusCode = Int64.Parse(deserializedMessage[0].ToString()); | |
string statusMessage = (string)deserializedMessage[1]; | |
string channelName = (string)deserializedMessage[2]; | |
if (statusCode == 1 && statusMessage.ToLower() == "sent") | |
{ | |
if (messagePublished.ContainsKey(channelName)) | |
{ | |
messagePublished[channelName] = true; | |
} | |
} | |
if (mrePublish.ContainsKey(channelName)) | |
{ | |
mrePublish[channelName].Set(); | |
} | |
} | |
} | |
} |
Once the poll answers are submitted to PubNub, the success status of message publish will be displayed as view to the web user as below:
@{ | |
Layout = null; | |
} | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta name="viewport" content="width=device-width" /> | |
<title>Demo ePoll</title> | |
<link rel="Stylesheet" href="../../Content/demo.css" /> | |
</head> | |
<body> | |
<div id="votecontainer"> | |
<p class="welcome">PubNub ePoll</p> | |
@if (ViewData["PollAnswerSaveStatus"] != null && (bool)ViewData["PollAnswerSaveStatus"]) | |
{ | |
if (ViewData["ID"] != null && ViewData["ID"].ToString() != "") | |
{ | |
string questionID = ViewData["ID"].ToString(); | |
using (Html.BeginForm("PollResult", "Poll",FormMethod.Post)) | |
{ | |
<p class="thanks">Thank you for your participation in PubNub ePoll</p> | |
@Html.Hidden("ID", questionID); | |
<div id="divpollresult2"> | |
<button name="btnPollResult" id="btnPollResult" value="View Poll Results">View Poll Results</button> | |
</div> | |
} | |
} | |
} | |
else | |
{ | |
<p>Technical error occured. Please try again later.</p> | |
} | |
</div> | |
</body> | |
</html> |
Subscribing to Realtime Voting User Submissions over WebSockets
The Subscribe<string>()
method is being used to receive poll answers in realtime via an automatically negotiated protocol like WebSockets, HTTP Streaming, HTTP Long-polling, TCP Streaming and more with automatic recovery of dropped messages. We’ve made it easy for you to get started by providing this easy-to-use SDK.
// Subscribe To Vote Submissions | |
pubnub.Subscribe<string>( | |
pubnubChannel, | |
DisplayReturnMessage, | |
DisplaySubscribeConnectStatusMessage, | |
DisplayErrorMessage | |
); | |
// Callback method Receive Votes in JSON string format | |
static void DisplayReturnMessage(string result) | |
{ | |
Console.WriteLine("VOTE RECEIVED!"); | |
Console.WriteLine(result); | |
Console.WriteLine(); | |
} | |
// Callback method to provide the connect status of Subscribe call | |
static void DisplaySubscribeConnectStatusMessage(string result) | |
{ | |
Console.WriteLine("SUBSCRIBE CONNECT CALLBACK:"); | |
Console.WriteLine(result); | |
Console.WriteLine(); | |
} | |
// Callback method for error messages | |
static void DisplayErrorMessage(PubnubClientError result) | |
{ | |
Console.WriteLine(); | |
Console.WriteLine(result.Description); | |
Console.WriteLine(); | |
} |
Loading History of Messages over WebSockets for Realtime Voting Dashboard
The DetailedHistory<string>
method is being used to pull the poll results. The following code retrieves the poll results.
public List<string> GetPollResults(string questionID) | |
{ | |
List<string> ret = null; | |
string pubnubChannel = questionID; | |
mreDetailedHistory.AddOrUpdate(pubnubChannel, new ManualResetEvent(false), (key, oldState) => new ManualResetEvent(false)); | |
detailedHistoryReceived.AddOrUpdate(pubnubChannel, false, (key, oldState) => false); | |
detailedHistoryStartTime.AddOrUpdate(pubnubChannel, 0, (key, oldState) => 0); | |
channelDetailedHistory.AddOrUpdate(pubnubChannel, new List<string>(), (key, oldState) => new List<string>()); | |
detailedHistoryReceived[pubnubChannel] = false; | |
long currentTimetoken = Pubnub.TranslateDateTimeToPubnubUnixNanoSeconds(DateTime.UtcNow); | |
detailedHistoryStartTime[pubnubChannel] = 0; // currentTimetoken; | |
while (!detailedHistoryReceived[pubnubChannel]) | |
{ | |
pubnub.DetailedHistory<string>(pubnubChannel, detailedHistoryStartTime[pubnubChannel], PollResultsRegularCallback, PollResultsErrorCallback, true); | |
mreDetailedHistory[pubnubChannel].WaitOne(); | |
if (!detailedHistoryReceived[pubnubChannel]) | |
{ | |
mreDetailedHistory[pubnubChannel].Reset(); | |
} | |
} | |
ret = channelDetailedHistory[pubnubChannel]; | |
return ret; | |
} |
And that’s it! We went through both the Publish and DetailedHistory calls for a realtime voting app.
Get Started
Sign up for free and use PubNub to power realtime voting