简体   繁体   中英

How to display the count of sent emails on the client while sending them on the server?

How to display the count of sent emails on the client while sending them on the server with ASP.NET (C#)?

So lets see my scenario:

  • I have a button inside an UpdatePanel.
  • This button should send some emails to my users (serverside).
  • When I click on that button I also want to do some stuff like this:

    1. show a modal div with low opacity with a label at the center of it(the text of that label is "0").
    2. start sending mails.
    3. that label's text should update to reflect every sent email (this is a counter)(0->1->2->...)
    4. at the end of sending mails I want to hide that modal div

And my code:

ASPX:

<asp:UpdateProgress ID="UpdateProgress1" runat="server">
  <ProgressTemplate>
    <asp:Image ID="imgAjax" runat="server" ImageUrl="~/Images/ajax-loader.gif" />
  </ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
  <ContentTemplate>
    <div id="RadbtnSendEmails_Container">
      <telerik:RadButton ID="RadbtnSendEmails" runat="server" EnableEmbeddedSkins="False"
        Skin="BlackByMe" Text="Send Mails" Width="150px" OnClick="RadbtnSendEmails_Click"
        Font-Bold="True" Font-Names="Tahoma" ValidationGroup="A">
      </telerik:RadButton>
    </div>
  </ContentTemplate>
</asp:UpdatePanel>
<div id="OffDiv">
</div>
<div id="CounterContainer">
  <asp:Image ID="imgWhiteCircle" runat="server" ImageUrl="~/Images/WhiteCircle.png" />
  <asp:Label ID="lblCounter" runat="server" Text="0" Font-Bold="True"
    Font-Size="50px" ForeColor="#6600CC">
  </asp:Label>
</div>

C

protected void RadbtnSendEmails_Click(object sender, EventArgs e) {
  ScriptManager.RegisterStartupScript(this, this.GetType(), "Show_Counter", 
    "Show_Counter();", true);

  //int Counter=0;
  //Send Mail Codes in for()
  //Counter++; in every send
  //Change that label'text to new counter -> i do n't know how can i do that

  //ScriptManager.RegisterStartupScript(this, this.GetType(), "Hide_Counter", 
    "Hide_Counter();", true);
}

However there is a problem with this C# codes:

When I click on that button, the image inside UpdateProgress shows itself and after this block ends sending emails we can see the modal div, but not at the start of the process!

Also, I don't know how to implement that counter! Should I jump from client-side code to server-side code or from server-side code to client-side code.

Those email addresses are in a Sql Server 2008R2 database. So I have to run that part of the process server side.

I really want to learn how fix this issue, and I think this scenario is nice, so please help me to get a good idea about that.

So the problem that you're facing is that you need to do some work serverside but you want to display that clientside. The first part of the problem is that HTTP is a stateless connectionless protocol, so you have to keep asking the server for more information (updates).

So what you need are several resources:

A way for the server to know what the current number of emails sent is (say a public counter that you can have access to), a method that the server runs to send the emails (this will generally need to be run on a background thread, but be careful, a crashed background thread - say from an unhandled exception - will collapse the entire app-pool) and a way to initialize the process from the client (you already have this part).

Then on the client you need a method to initiate requests, a method to check back for updates and know when you're done, and something to manage all the other things you wanted.

I'll assume you're using jQuery (which is a library on top of javascript, but does not replace javascript, which seems like it's something you need to learn about) and write some basic code here. I'll also assume you have the microsoft ASP.NET library running on the page (your code references ScriptManager so that's a pretty safe bet).

Let's start with the C#: (this goes in your page, I'm not going to write everything, just the large parts of it)

//this is a class you need to write, see below
//this way other code can access this variable too
public MySpecialMailerClass mailer;

[WebMethod] //this may require a include, you're on your own for that 
            //for now (System.Web.Services)
public void StartEmails(){
  //do some stuff here to start the email process?
  //create a class that can run the db scripts uninterrupted, 
  //and provide it a public member that can be accessed for the current
  //count, and the expected max count (set to -1 on init so you know 
  //that you haven't finished initting this yet)

  mailer = new MySpecialMailerClass();
  new Thread( mailer.Start ) { IsBackground = true }.Start( );
}

[WebMethod]
public int GetMaxCount(){
  return mailer.MaxCount;
}

[WebMethod]
public int GetCurrentCount(){
  return mailer.CurrentCount;
}

and that will actually be all of the C# code you need to add, for now.

Javascript:

var checkingOnEmailsSentIntervals,
    checkingOnMaxMessagesIntervals,
    maxMessages = 0;

function SuccessHandler(){
  //Note that you should do something here to handle the success each
  //time or that you should write a separate "SuccessHandler" 
  // for each one that you want to execute _when the server call is done_
  // Note that this is what I do (one success per call)
  // But in this case a generic one is sometimes handy, such as here
}

function FailHandler() {
  //Note that you should do something here to handle the failure each time
}

function startScript(){
  PageMethods.StartEmails(StartEmailsSuccessHandler,FailHandler);
}

function StartEmailsSuccessHandler(){
  //this will get triggered when the emails have started sending, 
  // so this is where you create the boxes you wanted earlier, etc.
  $('#counter-span-identifier').show();
  checkingOnEmailsSentIntervals = setInterval(CheckForMoreMessages,100);
  checkingOnMaxMessagesIntervals = setInterval(CheckForMaxMessages,10);
}

function CheckForMoreMessages() {
  PageMethods.GetCurrentCount(CheckForMoreMessagesSuccessHandler,FailHandler);
}

function CheckForMoreMessagesSuccessHandler(result) {
  var currentCount = result;
  $('#counter-span-identifier').text(currentCount + ' messages sent');
  if (currentCount == maxMessages) {
    //we have finished now, so we can display another message or hide our div
    clearInterval(checkingOnEmailsSentIntervals);
    $('#counter-span-identifier').hide();
  }
}

function CheckForMaxMessages() {
  PageMethods.GetMaxCount(CheckForMaxMessagesSuccessHandler,FailHandler);
}

function CheckForMaxMessagesSuccessHandler(result) {
  maxMessages = parseInt(result); //parseInt not really needed here, 
                                  //shown as example of coercion
  if (maxMessages > -1){
    clearInterval(checkingOnMaxMessagesIntervals);
  }
}

And that should pretty neatly wrap up the case here, as we've got two loops (setInterval) in javascript to check for the MaxMessages (hence returning -1) and for the CurrentCount. We've also got code to stop each loop once it gets the information it needs.

The part left for you to do is to put the code to send emails into a separate class that exposes to member variables and one method (and to rename my examples so they match your requirements).

And because I think it needs to be done, here's the C# for the MySpecialMailerClass to start you off:

public class MySpecialMailerClass {
  public MySpecialMailerClass() {
    MaxCount = -1;
    CurrentCount = 0;
  }
  public int MaxCount {get;set;}
  public int CurrentCount {get;set;}
  public void Start(){
    //do something to get the MaxCount from the database
    //do something to get the messages
    var messages = mailerDbClass.GetMessagesToBeSent();
    MaxCount = messages.Count;

    //send the messages
    foreach(message in messages) {
      SendMessage(message);
      CurrentCount += 1;
    }
  }
}

When you click your button on the client, a request is sent to the server. If you are using an update panel, this request is sent with ajax. Once the request is received by the server, it will run the code you have in your button click event handler. It will not send a response back to the client to update the page until the code in the click handler is finished.

What you are trying to do (At least the counter on the client side) is going to be just about impossible just using an update panel. If you can get over not having that, i'd say just skip it.

One possibility that i can think of would involve using a web service ( .asmx ) and ajax calls with custom javascript. You could send each individual email request to the web service, and when the service responds, update your modal with a count, then send the next request to the web service, and so on.

You need to understand that the browser cannot directly execute server code. Nor can the server execute code directly in the browser. You're best bet is to expose the methods you want to have executed from the browser via HTTP in some way (web services). Then use javascript and jQuery to call them and alter the UI in the manner you've described.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM