help to optimize large array of user reminders - c#

every night i have a trigger that executes asp.net page with method that first retrieves from DB users with needed details and then emails them with information they need like ie. users that does not have an image in their profile. Once i get data from DB i build large string with encoded HTML and assign it to MailMessage.Body tag. There are about 20000 users per action/night who get the mail. Stored procedures just get users based on criteria i need nothing to optimize there (after proper indexes been made).
How do i optimize it? Can i create sort of queue in ASP.NET that it will be executed all day long without one time action? Maybe i can use async actions to send few emails per time.
Need advice, if you have question about how i perform each action in order to help me don't be afraid to ask, i will answer i just don't know what place in code may interest you.
protected void Page_Load(object sender, EventArgs e)
{
string type = Request.QueryString["type"];
string spName = "";
string message = "";
switch (type)
{
//getUsersWithoutPictures
case "uwp":
spName = "getUsersWithoutPictures";
message = getUsersWithoutPicturesTemplate();
break;
//getUsersWithoutText
case "uwt":
spName = "getUsersWithoutText";
message = getUsersWithoutTextTemplate();
break;
//getUsersWithoutEnteringWebsiteForTwoWeeks
case "uwewftw":
spName = "getUsersWithoutEnteringWebsiteForTwoWeeks";
message = getUsersWithoutEnteringWebsiteForTwoWeeksTemplate();
break;
//getUsersWithoutHobbies
case "uwh":
spName = "getUsersWithoutHobbies";
message = getUsersWithoutHobbiesTemplate();
break;
//getUsersWithoutCharateristics
case "uwc":
spName = "getUsersWithoutCharateristics";
message = getUsersWithoutCharateristicsTemplate();
break;
//getUsersWithoutActivation
case "uwa":
spName = "getUsersWithoutActivation";
message = getUsersWithoutActivationTemplate();
break;
default:
Response.Write("failure");
Response.End();
break;
}
DataTable recipientsList = new DataTable();
using (SqlConnection cn = cms.connect("someconnectionstring"))
{
SqlDataAdapter adp = new SqlDataAdapter(spName, cn);
adp.SelectCommand.CommandType = CommandType.StoredProcedure;
adp.Fill(recipientsList);
}
foreach (DataRow row in recipientsList.Rows)
{
try
{
IPHostEntry emailHost = Dns.GetHostEntry(row["email"].ToString().Remove(0, row["email"].ToString().LastIndexOf("#") + 1));
MailMessage myMessage = new MailMessage(new MailAddress("support#" + row["registratioSite"].ToString()), new MailAddress(row["email"].ToString()));
myMessage.Subject = "";
myMessage.Body = getGenericEmailTemplate(AffDomains.getFullDomain(row["registratioSite"].ToString()), message, row["email"].ToString(), row["usernumber"].ToString(), row["nick"].ToString());
myMessage.IsBodyHtml = true;
SmtpClient mySmtp = new SmtpClient("mysmtp.server.com");
mySmtp.Send(myMessage);
}
catch (Exception)
{
}
}
Response.Write("success");
Response.End();
}
private string getGenericEmailTemplate(string domain, string message, string email, string usernumber, string nick)
{
return "some html";
}
private string getUsersWithoutPicturesTemplate()
{
return "some message";
}
private string getUsersWithoutTextTemplate()
{
return "some message 2";
}
private string getUsersWithoutEnteringWebsiteForTwoWeeksTemplate()
{
return "some message 3";
}
private string getUsersWithoutHobbiesTemplate()
{
return "some message 4";
}
private string getUsersWithoutCharateristicsTemplate()
{
return "some message 5";
}
private string getUsersWithoutActivationTemplate()
{
return "some message 6";
}

Some pointers based on your current implementation:
Do this once per week. Is your site really so important that the users must be tortured every single night?
It looks like you're going to email each user several times, eg if they don't have a picture and haven't entered for two weeks they will get two emails. Combine all the events into one email.
Batch your emails into small groups based on which notifications they ought to receive, eg BCC 5 users at a time who don't have images in their profile.
Return only the data you need from the stored procs.
Move everything you can outside of the main loop eg creation of the EmailHost, MailMessage object and set the relevant properties inside the loop.
If you want this solution to scale way beyond its current limits however, you might want to think about some sort of multithreaded (or distrubuted) producer/consumer queue. I won't elaborate further here - there are plenty of examples on the web.

Related

gRPC: How to use dynamically typed values?

I'm trying using gRPC dynamically typed values but with the little information about their usefulness, It's almost impossible to do this... So I will show the image/code that I have problems and the questions that are eating my brain
gRPC Method I'm doing:
public override Task<HelloReply2> TestObject(Status request, ServerCallContext context) {
//The part I may have problems
var status = new Status();
//here I want a User that corresponds to my request.Data
//example -> request.Data = User1 (gives me null if User1 don`t exist in db)
// request.Data = 14 (gives me null if 14 don`t exist in db)
// request.Data = true (gives me null if true don`t exist in db)
var a1 = _context.Users_5.FirstOrDefault(x => x.Username.Equals(request.Data));
var b1 = _context.Users_5.FirstOrDefault(x => x.Email.Equals(request.Data));
var c1 = _context.Users_5.FirstOrDefault(x => x.Age.Equals(request.Data));
var d1 = _context.Users_5.FirstOrDefault(x => x.Test.Equals(request.Data));
//is a bool
//here i want too Create dynamic values
status.Data = Value.ForStruct(new Struct {
Fields =
{
["Integer"] = Value.ForNumber(c1!.Age),
["StringName"] = Value.ForString(a1!.Username),
["StringEmail"] = Value.ForString(b1!.Email),
["Boolean"] = Value.ForBool(d1!.Test)
}
});
//Below is just a simple string who gives different string (depending on the
//data Status (also how to read the message from the status.Data ?)
HelloReply2 hello = new();
if(a1 != null)
{
hello.Message = "There is a User with the Username " + request.Data + ". His Email is " + a1.Email;
} else if (b1 != null) {
hello.Message = "There is a User with the Email " + request.Data + ". His Username is " + b1.Username;
}
else if (c1 != null)
{
hello.Message = "There is at least one User with that Age of " + request.Data + ". His Username is " + c1.Username;
}
else if (d1 != null)
{
if(d1.Test == true)
{
hello.Message = "There is at least one User who dislikes chocolate: " + request.Data + ". His Username is " + d1.Username;
} else
{
hello.Message = hello.Message = "There is at least one User who likes chocolate: " + request.Data + ". His Username is " + d1.Username;
}
}
else
{
hello.Message = "We didn't find something with the value that the User put in. Value:" + request.Data;
}
return Task.FromResult(hello);
}
Questions: How to Get the one Value from my gRPC? How to convert a "Object" in c# (one string, one integer or one List) into a ONE value of google.protobuf.Value (so it not give me errors like this Controller from a Web Api below)? Is something wrong with my gRPC Service Method (is something wrong reading the dynamic values? Can I do that calls for getting a User for a DB? How to read dynamic values?)
// I try using Google.Protobuf.WellKnownTypes.Value obj but
//not workings because gives me a lot of values to put
[HttpGet("TypeObject/{obj}")]
public async Task<ActionResult<HelloReply2>> TypeObject([FromRoute] Object obj){
Status objRequest = new Status { Data = (Google.Protobuf.WellKnownTypes.Value)
obj };
//cannot do this (gives me error of casting but the ideia is doing something
//similar to this)
var hello = await _greetClient.TestObjectAsync(objRequest);
return Ok(hello);
}
Any help on how to resolve this error of using Value gRPC or if is something wrong with the code is always welcome.
Edit:
One day after this question I don't have any solutions / progress. I was think of doing Any or OneOf for testing but it also gives me errors (who don't make sense at all). This code from Microsoft (C# Format part is not recognize) doesn't work in my project with the protos reload (the problem is not in the protos)
Link: https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/protobuf-any-oneof
How I can use Any / OneOf without give me error in the Formating? What is the difference between Value and this two? Can the three (Value, Any, OneOf) be dynamic/Object values (if yes how to convert the types)?
Edit 2:
Still have problems, I'm trying using gRPC Any , and maybe have some progress (not all).
So with Any I have my method in the server gRPC and it is like this
public override Task<HelloReply2> TestObject3(AnyMessage request, ServerCallContext context){
HelloReply2 anyMessageResponse;
var y = request.TypeUrl;
switch (request.TypeUrl)
{
case "type.googleapis.com/any.HelloRequest":
var string_1 = request.Unpack<HelloRequest>();
anyMessageResponse = new HelloReply2{
Message = "You type String: " + $"{string_1.Name}"
};
break;
case "type.googleapis.com/any.TestInteger1":
var integer_1 = request.Unpack<TestInteger1>();
anyMessageResponse = new HelloReply2{
Message = "You type Integer: " + $"{integer_1.Message}"
};
break;
case "type.googleapis.com/any.TestBool1":
var bool_1 = request.Unpack<TestInteger1>();
anyMessageResponse = new HelloReply2{
Message = "You type Bool: " + $"{bool_1.Message}"
};
break;
default:
throw new InvalidOperationException("Unexpected type URL.");}
return Task.FromResult(anyMessageResponse);
}
This ideia comes from here (https://github.com/grpc/grpc-dotnet/issues/917), but the client part their don't have any much info or I don't understand that part
This is what I did in the WebApi (who is my client and the code is similar to the above one)
using AnyMessage = Google.Protobuf.WellKnownTypes.Any;
[HttpGet("TypeObject3/{obj3}")]
public async Task<ActionResult<HelloReply2>> TypeObject3([FromRoute] string obj3)
{
AnyMessage objRequest = new() { TypeUrl = obj3 };
var hello = await _greetClient.TestObject3Async(objRequest);
var l = hello.Message;
return Ok(hello);
}
First I had the variable Any declared in the method instead of string but as you can only put string and stringBytes so I preferred to put it like this (with the string as an obj3 variable) but my goal is to see if the variable is of type TestBool1 or TestInteger1 as I have declared in the protos and not be a string that I will be able to see, and the biggest problem was if I had more variables inside the messages how to proceed? So my secondary question is how to use Any on the client side via the Web-Api? I forgot to say but I'm using .Net 6 Core and for testing I'm using Swagger, where at this moment my error hits the Exception dictated by the Server method.
Questions: Why TypeUrl is a string and not object? How to fix my problem? How to test the object type (or string) for more values if the messages was with 1 more types?
Also I will show my test proto too show how I'm doing this
import "google/protobuf/struct.proto";
import "google/protobuf/any.proto";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayNormalHello (Empty_2) returns (HelloReply);
rpc SayHello (HelloRequest) returns (HelloReply2);
rpc TestInt (TestInteger1) returns (HelloReply2);
rpc TestBoolean (TestBool1) returns (HelloReply2);
rpc TestObject (Status) returns (HelloReply2); //Not working
rpc TestObject2 (Status2) returns (HelloReply2); //Not working
rpc TestObject3 (google.protobuf.Any) returns (HelloReply2); //Also
//Not working
}
message Empty_2{
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
// The response message containing the greetings.
message HelloReply2 {
string message = 1;
}
message TestInteger1 {
int32 message = 1;
}
message TestBool1 {
bool message = 1;
}
message Status {
google.protobuf.Value data = 1;
}
message Status2 {
google.protobuf.Any data = 1;
}
Any help is welcome.
In the project I developed, I needed an infrastructure where I could perform dynamic operations such as REST service using gRPC. As I understand it, what you want is something similar to this.
I have developed a solution for this. It might work for you too.
You can gain some flexibility by making a definition with a single String field for Requests and Responses that you want to be Dynamic, and then using this field to hold JSON data as strings.
For example, instead of defining different responses for different types, you can solve it by making one definition like this.
message HelloReply {
// Stringified JSON Data
string data = 1;
}

Bug in SSRS CreateFolder C# command?

I think I've come across a bug in the CreateFolder command in the Reportingservices2010 SOAP API
The test scenario is I'm trying to create a folder (named Sales Dashboard) in the same Parent folder (lets say Sales) as a report also named Sales Dashboard.
The command completed with the "AlreadyExists" Exception when the folder does not already exist. It looks like the method isn't checking the catalog item type.
Here's my code:
public static void createFolders(string targetURL, string folderName, string parentPath, string description, string visible)
{
//Build Authentication
ReportingService2010 rs = new ReportingService2010();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
rs.Url = targetURL;
//Declare properties
Property descriptionProp = new Property();
Property visibleProp = new Property();
Property[] props = new Property[2];
descriptionProp.Name = "Description";
descriptionProp.Value = description;
visibleProp.Name = "Visible";
visibleProp.Value = visible;
props[0] = descriptionProp;
props[1] = visibleProp;
try
{
rs.CreateFolder(folderName, parentPath, props);
}
catch(Exception ex)
{
if(ex.Message.Contains("AlreadyExists"))
{
//do nothing?
}
else
{
throw;
}
}
}
I wanted to see if I could contribute a fix but there's no GitHub repo for the C# SSRS stuff. Any thought's on a workaround?
The API is returning the correct error since this is a restriction of Reporting Services in general: items within the same folder must have unique names (regardless of item type).

C# Sockets send/receive problems and questions

Good morning all,
First of all apologies for the somewhat generic title. If in the course of this message I can come up with something a little more detailed, I will certainly change it.
I am working on a project that will contain 3 programs. The purpose is to be able to send a notification to all clients connected. For this there is a server, client, and console program.
The message itself will be an RTF file, but the notification also requires a sending department (string), and a display timer (TimeSpan).
Most of the project is done. The logic is mostly complete, it is multithreaded, all the classes are ready, and most tests work.
The problem I am having is that the server seems to not receive the data from the console in the right order. Thereby causing all sorts of problems.
The process from console to server is as follows:
Console first selects all relevant information for the notification:
Users (List, gathered from LDAP)
Expiration date and time (DateTime)
Time before close is allowed (long, amount of ticks)
Department (string)
RTF file
The server is already active, and has a separate thread for the console connections
Console connects to server
Server creates a separate thread to handle the console connection
Console sends the username of the logged on user to the server
Server checks whether user is allowed to create notification (for now it will always return true)
Console sends all the relevant information in the following order:
MessageID (string)
RecipientCount (number of users, used to loop appropriately from server side)
Recipients (foreach loop of List)
Department (string)
VisibleTime (long)
Expiration (DateTime)
and finally RTF file
I have used System.Diagnostics.Trace to check whether all information is sent correctly, and in the right order. This all checks out. But the thing is that roughly 75% of the time the server side seems to receive the RTF file at the moment it should be receiving the visibleTime.
The code is as follows:
private void SendMessage()
{
SendToServer(Environment.UserName);
if (bool.Parse(ReadFromServer()))
{
// User is allowed, continue
string messageID = DateTime.Now.ToUniversalTime().Ticks.ToString();
SendToServer(messageID); // MessageID
string recipientCount = lvRecipients.Items.Count.ToString();
SendToServer(lvRecipients.Items.Count.ToString()); // Amount of recipients
foreach (string item in lvRecipients.Items) // Loop to send each recipient
{
SendToServer(item);
}
string department = TB_Department.Text;
SendToServer(department); // Send department string
string visibleTime = TimeSpan.FromSeconds(SLIDER_VisibleTime.Value).Ticks.ToString();
SendToServer(visibleTime); // Send message visibility time
string expiration = DateTime.Now.ToUniversalTime().AddMinutes(2).ToString();
SendToServer(expiration); //TODO add UI control for this
SendRTFToServer(); // Send RTF file
MessageBox.Show(
"Your designated MessageID is: " + messageID + Environment.NewLine +
"Message upload is succesful.",
"Complete",
MessageBoxButton.OK);
}
else
{
// User is not allowed. Report to user. Disconnect (will be managed by the finally block)
MessageBox.Show(
"You are not allowed to upload messages to the server.",
"Access denied",
MessageBoxButton.OK,
MessageBoxImage.Stop);
return;
}
}
private void SendToServer(string toSend)
{
StreamWriter writer = new StreamWriter(server.GetStream());
writer.WriteLine(toSend);
writer.Flush();
}
private void SendRTFToServer()
{
StreamReader rtfFile = new StreamReader(File.Open(RTFLocation, FileMode.Open, FileAccess.Read));
StreamWriter sw = new StreamWriter(server.GetStream());
sw.Write(rtfFile.ReadToEnd());
sw.Flush();
server.GetStream().Flush();
}
private string ReadFromServer()
{
server.GetStream().Flush();
StreamReader reader = new StreamReader(server.GetStream());
return reader.ReadLine();
}
And from the server:
private void Connect()
{
string username = ReadFromConsole();
if (IsUserAllowed(username)) // Receive username
SendToConsole(bool.TrueString); // Send confirmation
else
{
SendToConsole(bool.FalseString); // Send denial
console.Close();
return;
}
string messageID = ReadFromConsole(); // Receive MessageID
string recipientCount = ReadFromConsole();
int numOfRecipients = int.Parse(recipientCount); // Receive and parse number of recipients
List<string> recipients = new List<string>();
for (int i = 0; i < numOfRecipients; i++)
{
string recipient = ReadFromConsole();
recipients.Add(recipient); // Receive recipient, add to list (required for Message)
}
string department = ReadFromConsole(); // Receive department string
string visibleTime = ReadFromConsole();
string expiration = ReadFromConsole();
StoreRTF(messageID); // Receive and store RTF file
console.Close(); // Connection is done, close
Message message = new Message(messageID, department, recipients, visibleTime, expiration);
}
private void SendToConsole(string toSend)
{
// Open client stream, and write information to it.
StreamWriter writer = new StreamWriter(console.GetStream());
writer.WriteLine(toSend);
writer.Flush();
}
private string ReadFromConsole()
{
// Read information from client stream
StreamReader reader = new StreamReader(console.GetStream());
return reader.ReadLine();
}
private void StoreRTF(string messageID)
{
// Check/create folder for Message storage
string messageFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\BMNotify\";
if (!Directory.Exists(messageFolder))
Directory.CreateDirectory(messageFolder);
// Create file to store message in
Stream rtfFile = File.Create(messageFolder + messageID + ".rtf");
// Store information from stream, and close resources
console.GetStream().CopyTo(rtfFile);
rtfFile.Close();
rtfFile.Dispose();
}
And the message class:
public class Message
{
internal string messageID;
internal string department;
internal List<string> recipients;
internal TimeSpan visibleAtLeast;
internal DateTime messageExpiration;
private static List<Message> allMessages; // Will hold te collection of Message's
public Message(string _messageID, string _department, List<string> _recipients, string visibleTime, string expiration)
{
messageID = _messageID;
recipients = _recipients;
department = _department;
visibleAtLeast = TimeSpan.FromTicks(long.Parse(visibleTime));
messageExpiration = DateTime.Parse(expiration);
if (allMessages == null)
allMessages = new List<Message>(); // Initialize if required
allMessages.Add(this);
}
internal Stream GetRTF()
{
return File.Open
(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\BMNotify\" + messageID + ".rtf",
FileMode.Open,
FileAccess.Read,
FileShare.Read);
}
static public List<Message> AllMessages()
{
if (allMessages == null)
allMessages = new List<Message>(); // Initialize if required
return allMessages;
}
static public void RemoveMessage(Message message)
{
allMessages.Remove(message);
}
}
If anyone could shed any light on this, or tell me what I should change.. Or basically anything that could get me going again, I would be very grateful!
Your problem may stem from the fact that you're using a StreamReader to read data from the connection. Internally StreamReader buffers data it reads from the underlying Stream. You are creating a new StreamReader every time you attempt to read from the connection, and then discarding it once you've read a single line. In doing so, you're also discarding any data read from the connection that was buffered by the StreamReader (which may have constituted all or part of the following fields).
You should probably attempt to create a single StreamReader on the network stream, and use that same instance for all reads.

c# asp.net send mail issue

I have create one web application where user can send friend request. If there are two users A and B. If A user send friend request to B then one pop up display's on B user dashboard. In that pop up box two buttons are there confirm or ignore.
If user click on confirm button then it takes some time to close that popup box. Because in backgroud it execute code for send email. Once email sent then that popup box close. So I want to close that popup box immediately after clicking on confirm button and then after send mail to that user.
Here is my code for accepting request and sending mail
#region dlUserFriendRequests_ItemCommand
protected void dlUserFriendRequests_ItemCommand(object source, DataListCommandEventArgs e)
{
HtmlTable objDataTable;
//Panel objDataTable;
switch (e.CommandName)
{
case "confirm":
RadioButtonList objRadioButtonList;
int intFriendRelationID = -1;
objRadioButtonList = (RadioButtonList)e.Item.FindControl("rblstFriends");
if (objRadioButtonList != null)
{
intFriendRelationID = UserManagementBL.AcceptUserFriendRequest(Convert.ToInt32(e.CommandArgument), this.LoginSessionDO.UserID, objRadioButtonList.SelectedItem.Text);
if (intFriendRelationID > 0)
{
int SentByUserID = Convert.ToInt32(e.CommandArgument);
DataTable dtbSenderdetails = null;
string SenderEmail = "";
dtbSenderdetails = UserManagementBL.GetUserDetails(SentByUserID);
if (dtbSenderdetails != null)
{
SenderEmail = dtbSenderdetails.Rows[0]["UserName"].ToString();
SendConfirmationMail(SenderEmail);
Response.Redirect("~/Dashboard/Dashboard.aspx",false);
//GetUserFriendRequests();
}
}
}
break;
case "Ignore":
int intFriendRequestID = -1;
intFriendRequestID = UserManagementBL.IgnoreUserFriendRequest(Convert.ToInt32(e.CommandArgument), this.LoginSessionDO.UserID);
GetUserFriendRequests();
break;
}
}
#endregion
#region Send confirmation mail
public void SendConfirmationMail(string email)
{
//DataTable dtblUserDetails = UserManagementBL.GetUserByUserName(email);
//if (dtblUserDetails != null)
//{
//int UserID = Convert.ToInt32(dtblUserDetails.Rows[0]["UserID"]);
//string FirstName = Convert.ToString(dtblUserDetails.Rows[0]["FirstName"]);
//string LastName = Convert.ToString(dtblUserDetails.Rows[0]["LastName"]);
string FirstName = this.LoginSessionDO.FirstName;
string LastName = this.LoginSessionDO.LastName;
var parameters = new System.Collections.Generic.Dictionary<string, string>();
parameters.Add("USER_NAME", string.Format("{0} {1}", FirstName, LastName));
parameters.Add("USER_IMAGE_URL", string.Format(SystemConfiguration.GetSiteURL() + "UserControls/UserPhoto.ashx?UserID={0}", this.LoginSessionDO.UserID));
string ToAddress = email;
string subject = FirstName + " " + LastName + " confirmed you as a friend on Lifetrons.";
CommonFunctions.CommonFunctions.SendEmail(SystemConfiguration.GetEmailSenderAddress(), ToAddress, subject, CommonFunctions.EmailTemplates.AcceptFriendRequest, parameters);
//}
}
#endregion
Here is my pop up box image
So how can I close that pop up box immediately after confirm button click? Is there any changes in my code?
You would do this in JavaScript. I assume you're already using AJAX to perform the Confirm action, otherwise it would just be reloading the page and your popup shouldn't be there anyway (since they're already confirmed?).
If you have jQuery on the frontend, you can use:
$('#confirm-box-id').hide();
Without jQuery, you could use:
document.getElementById('confirm-box-id').style.display = 'none';
Re-reading your message, it seems this is just a long running action. You should note that if you do hide this and don't show any indication of progress using the code above, for example, your user may navigate away or close the browser, which may cause the action to stop processing or be terminated forcefully server-side so the confirmation would never happen.

What is wrong with my program logic

Please help me find the defect in my logic. I have two variables named "prev" and "next"...What i am basically doing is reading the data from my database every 5s and printing it out using Websync server if next and prev are NOT equal. I have two rows in my database . It looks like
ID
8
10
Here is the link to the code http://pastebin.com/Hb3eH2Qv
When i run my program, i get the result as
8 10 8 10
8 10
8 10 8 10
8 10
..... (so on)
But, the result should be just
8 10
I dont know how 8 10 8 10 appears. Data gets concatenated twice.
NOTE: You can just see the code in PublishLoop() function
private void PublishLoop()
{
String prev=String.Copy("");
String next=String.Copy("");
String ConnectionString = ConfigurationManager.ConnectionStrings["MyDbConn"].ToString();
SqlConnection connection = new SqlConnection(ConnectionString);
SqlCommand command = connection.CreateCommand();
command.CommandText = "select ID from Tab1";
command.Notification = null;
while (Running)
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
StreamWriter sw1 = new StreamWriter("C:\\Users\\Thothathri\\Desktop\\next.txt");
while ((reader.Read()))
{
//Response.Write(reader[0].ToString());
next = String.Concat(next,reader[0].ToString());
sw1.WriteLine(next);
}
sw1.Close();
if (!prev.Equals(next))
{
Publisher publisher = new Publisher(new PublisherArgs
{
DomainKey = "c80cb405-eb77-4574-9405-5ba51832f5e6",
DomainName="localhost"
});
Publication publication = publisher.Publish("/test", JSON.Serialize(next));
if (publication.Successful == true)
{
StreamWriter sw = new StreamWriter("C:\\Users\\Thothathri\\Desktop\\error123.txt");
sw.WriteLine("success");
sw.WriteLine(next);
sw.Close();
}
else
{
StreamWriter sw = new StreamWriter("C:\\Users\\Thothathri\\Desktop\\error123.txt");
sw.Write("failed");
sw.Close();
}
prev = String.Copy(next);
next = String.Copy("");
}
}
Thread.Sleep(5000);
}
}
Renuiz answered it in a comment, but it is because you're not clearing next.
So you build the string "8 10" in next, store it in prev. Next time you concat "8 10" with next, making "8 10 8 10". Which is different so you print it.
if (!prev.Equals(next))
{
....
prev = String.Copy(next);
next = String.Copy("");
}
This is the end of that loop. You should really be clearing next at the beginning of that loop.
Also you can just set the string
next = String.Empty;
I would declare next inside your while loop, as you don't need it in the greater scope, and I would call it current rather than next.
What is really wrong with your program logic - logic is not obvious. It is so obscure, that you can't understand where error is. So, my advise is following - if you can't find the error, try to simplify your code.
Currently your method has many responsibilities - it queries database, it dumps data to file, it publishes data somewhere and logs the results. And you stuck with all that stuff. If someone will need to change database query, or publishing logic - he will need to review all other stuff.
So, separate logic first:
private void PublishLoop()
{
string previousIDs = String.Empty;
int timeout = Int32.Parse(ConfigurationManager.AppSettings["publishTimeout"]);
while (Running)
{
string currentIDs = ConcatenateList(LoadIDs());
Dump(currentIDs);
if (!previousIDs.Equals(currentIDs))
{
try
{
Publish(currentIDs);
_log.Info("Published successfuly");
}
catch (PublicationException exception)
{
_log.Error("Publication failed");
}
previousIDs = currentIDs;
}
Thread.Sleep(timeout);
}
}
Well, I don't know much about your domain, so you probably can think about better names for variables and methods.
Here you have data access logic extracted to separate method (it's ok for first step of refactoring and for small applications). Keep in mind, that wrapping connection object into using block guarantee that connection will be closed in case of exception:
private IList<int> LoadIDs()
{
List<int> ids = new List<int>();
String connectionString = ConfigurationManager.ConnectionStrings["MyDbConn"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = connection.CreateCommand();
command.CommandText = "select ID from Tab1";
command.Notification = null;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while ((reader.Read()))
ids.Add((int)reader["ID"]);
}
}
return ids;
}
Next - simple method for concatenating ids into one string:
private string ConcatenateList(IList<int> values)
{
return String.Join(" ", values.Select(value => value.ToString()).ToArray());
}
Dumping (mind, that file name moved to configuration file):
private void Dump(string ids)
{
using (StreamWriter writer = new StreamWriter(ConfigurationManager.AppSettings["dumpFilePath"]))
writer.WriteLine(ids);
}
And publishing logic:
private void Publish(string ids)
{
PublisherArgs args = new PublisherArgs
{
DomainKey = "c80cb405-eb77-4574-9405-5ba51832f5e6",
DomainName = "localhost"
};
Publisher publisher = new Publisher(args);
Publication publication = publisher.Publish("/test", JSON.Serialize(ids));
if (!publication.Successful)
throw new PublicationException();
}
I think that failures are exceptional and they not occur very often (so I decided to use exceptions for that case). But if it's something ordinary - you can simply use boolean method like TryPublish.
BTW you can use some logging library like log4net for logging successful and failure publishing. Or you can extract logging logic to separate method - this will make primary logic cleaner and easier to understand.
PS try to avoid comparison of boolean variables with true/false (publication.Successful == true) - you can occasionally assign value to your variable.

Categories