Senior Content Strategist, PubNub
IN THIS ARTICLE


    realtime voting app

    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:

     

    simple realtime voting app

    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
    view raw 1.cs hosted with ❤ by GitHub

    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>
    view raw 2.cs hosted with ❤ by GitHub
    <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>
    view raw 3.cs hosted with ❤ by GitHub

    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();
    }
    }
    }
    }
    view raw 4.cs hosted with ❤ by GitHub

    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>
    view raw 5.cs hosted with ❤ by GitHub

    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();
    }
    view raw subscribe.cs hosted with ❤ by GitHub

    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;
    }
    view raw 6.cs hosted with ❤ by GitHub

    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

    Try PubNub today!

    Build realtime applications that perform reliably and securely, at global scale.
    Try Our APIs
    Try PubNub today!
    More From PubNub