Java Email: Getting the name to print next to the email address - jakarta-mail

Using Java to send an email, how would I get the name to print next to email address, preferably in this style?:
Joe A. Blow <joe.a.blow#host.org>
I have my email lists in in a *.properties file with the data like this:
email_list_3 = Bob Smith<bob.smith#acme.com>,Jane Doe<jane.doe#acme.com, Betty Crocker<betty.crocker#acme.com
I'm using this function to return each email list in the properties file, like "email_list_3", as an array list:
private static ArrayList setEmailList(String email_list_name)throws Exception {
Properties props = new Properties();
ArrayList email_list = new ArrayList();
try {
props.load(MailSender.class.getResourceAsStream("/email_lists.properties"));
email_list.addAll(Arrays.asList(props.getProperty(email_list_name).split(",")));
}// end try
catch (Exception e) {
logger.error("Failure populating email list: " + email_list_name + "\n",e);
}
return email_list;
}// end function setEmailLists()
Next I will collect the email addresses ( Im assuming the names are still with them? ) into an array of InternetAddresses like this
ArrayList<String> recipientsArray = null;
ArrayList<InternetAddress> addressTo = new ArrayList<InternetAddress>();
recipientsArray = getAnArrayListOfEmailAddresses();
for (String tempAddress: recipientsArray) {
if (tempAddress != null) {
addressTo.add(new InternetAddress(tempAddress));
}
}
And this is how I would plug my addresses into a email message:
InternetAddress[] to = new InternetAddress[addressTo.size()];
to = addressTo.toArray(to);
// processed in a for loop
tempAddress = (to[i]).getAddress();
MimeMessage msg = null;
msg = new MimeMessage(session);
msg.setSubject(subject);
msg.setFrom(new InternetAddress(fromAddressStringl));
msg.setRecipients(Message.RecipientType.TO, tempAddress);
To me, right now, it looks like to get the names displayed next to the email addresses I have use the other constructor for InternetAddress, that takes two arguments, address and name.
Is that right or is there a setting I can change such that when Java parses the name + address combination items out of my file it will do it automagically?
Thanks much in advance
Steve

You're making this too complicated.
You have a bunch of InternetAddress objects, just use them directly to set the recipients:
InternetAddress[] to = new InternetAddress[addressTo.size()];
to = addressTo.toArray(to);
MimeMessage msg = new MimeMessage(session);
msg.setSubject(subject);
msg.setFrom(new InternetAddress(fromAddressStringl));
msg.setRecipients(Message.RecipientType.TO, to);
It's your use of getAddress() that separates the address field from the personal name field; don't do that.
Also, you can use the InternetAddress.parse() method to parse your property into an array of InternetAddress objects, rather than splitting on commas yourself.

Related

SSIS - Sending email different recipients different data

I'm new in SSIS.
I have a table with 1500 rows and I need to send emails from that table but each recipients has 15 rows from the table.
So I need to send different data to different emails from the same query.
Can you guys could help me please?
Thanks in advance.
Leo
-------------------update------------------------
Guys I could create a SSIS package to send email to different recipients the problem is: sample: 2 different users is receiving emails for the number of rows that they have in database...that's terrible each customer has 15 lines it will be 15 emails can I send just one email for customer contains the whole data?
Thanks in advance...
This is going to vary somewhat based on the query and other specifications, but at a high level you're probably going to want to follow these steps for sending the emails using SSIS. This example assumes that the emails are stored in a column within this table. As others have pointed out, using sp_send_dbmail will likely be your best option.
Create two string variables. One will hold the email addresses and the other will be for the SQL for sp_send_dbmail (more on this below). Create an additional variable of the object type that will hold the list of emails during execution.
Modify the string variable that will hold the SQL for sp_send_dbmail to be an expression using the variable with the email names. Depending on the query, you may need to add additional variables for other parameters in this query. An example of this variable is at the end of this post.
Have an initial Execute SQL Task that queries the table and retrieves the email addresses. Make sure to get all rows for each email. Set the ResultSet property to full and on the Result Set pane, add the object variable with 0 as the Result Name.
Next add a Foreach Loop, use the Foreach ADO Enumerator type, and select the object variable from the last Execute SQL Task for the source variable. The Enumeration Mode can be left as the "Rows in the first table" option.
On the Variable Mappings pane, add the string variable (for the email addresses) and set the index to 0. This will hold the email addresses for each execution of sp_send_dbmail.
Within the Foreach Loop, add an Execute SQL Task. For this, you will need to set the SQLSourceType to variable and use a variable holding the SQL with sp_send_dbmail.
Make sure that you have Database Mail properly configured for the account and profile used, including membership in the DatabaseMailUserRole role in msdb. You may also need to use the three-part name (database.schema.table) for your table.
Example SQL Variable Expression:
Note the double-quotes in the #query parameter around the email variable in addition to the quotes from concatenating the expression. You can either use two single quotes or precede a double-quote with a \ in the query to use a double-quote as part of the expression.
"DECLARE #Title varchar(100)
SET #Title = 'Email Title'
EXEC MSDB.DBO.SP_SEND_DBMAIL #profile_name = 'Your Profile',
#recipients = 'YourEmail#test.org',
#query = 'SELECT * FROM YourDatabase.YourSchema.YourTable WHERE EmailColumn = ""
+ #[User::VariableWithEmailAddress] + ""',
#query_result_no_padding = 1, #subject = #Title ; "
I have a package that sole role is to send emails from my packages and record the results in to a table. I use this package over and over from any package that sends mail.
It is simply a script task, that takes parameters and does the work:
The script to process:
public void Main()
{
//Read variables
#region ReadVariables
string cstr = Dts.Variables["connString"].Value.ToString();
//string sender = (string)Dts.Variables["User::Sender"].Value;
string title = (string)Dts.Variables["$Package::Title"].Value;
string priority = (string)Dts.Variables["$Package::Priority"].Value;
string body = (string)Dts.Variables["$Package::Body"].Value;
string source = Dts.Variables["$Package::Source"].Value.ToString();
string directTo = Dts.Variables["$Package::DirectMail"].Value.ToString();
string groups = Dts.Variables["$Package::MailGroups"].Value.ToString();
#endregion
//Send Email
#region SendMail
MailMessage mail = new MailMessage();
//mail.From = new MailAddress(sender);
mail.Subject = title;
mail.Body = body;
mail.IsBodyHtml = true;
switch(priority.ToUpper())
{
case "HIGH":
mail.Priority= MailPriority.High;
priority = "High";
break;
default:
mail.Priority=MailPriority.Normal;
priority = "Normal";
break;
}
DataTable dt = new DataTable(); //This is going to be a full distribution list
//Fill table with group email
if (groups.Split(',').Length > 0)
{
foreach (string group in groups.Split(','))
{
string strCmd = "mail.spGetEmailAddressesByGroup";
using (OleDbConnection conn = new OleDbConnection(cstr))
{
using (OleDbCommand cmd = new OleDbCommand(strCmd, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("A", group);
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
}
}
}
}
//add the directs to email
if (directTo.Split(',').Length > 0)
{
foreach (string m in directTo.Split(','))
{
if (m != "")
{
DataRow dr = dt.NewRow();
dr[0] = "TO";
dr[1] = m;
dt.Rows.Add(dr);
}
}
}
//Add from and reply to defaults
DataRow dr2 = dt.NewRow();
dr2[0] = "REPLYTO";
dr2[1] = ""; //WHERE DO YOU WANT REPLIES
dt.Rows.Add(dr2);
DataRow dr3 = dt.NewRow();
dr3[0] = "FROM";
dr3[1] = ""; //ENTER WHO YOU WANT THE EMAIL TO COME FROM
dt.Rows.Add(dr3);
//Bind dt to mail
foreach (DataRow dr in dt.Rows)
{
switch (dr[0].ToString().ToUpper())
{
case "TO":
mail.To.Add(new MailAddress(dr[1].ToString()));
dr[0] = "To";
break;
case "CC":
mail.CC.Add(new MailAddress(dr[1].ToString()));
dr[0] = "Cc";
break;
case "BCC":
mail.Bcc.Add(new MailAddress(dr[1].ToString()));
dr[0] = "Bcc";
break;
case "REPLYTO":
mail.ReplyToList.Add(new MailAddress(dr[1].ToString()));
dr[0] = "ReplyTo";
break;
case "FROM":
mail.From = new MailAddress(dr[1].ToString());
dr[0] = "From";
break;
case "SENDER":
mail.Sender = new MailAddress(dr[1].ToString());
dr[0] = "Sender";
break;
default:
dr[0] = "NotSent";
break;
}
}
try
{
SmtpClient smtp = new SmtpClient();
smtp.Port = 25;
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.UseDefaultCredentials = false;
smtp.Host = ""; //ENTER YOUR IP / SERVER
smtp.Send(mail);
}
catch (Exception e)
{
}
#endregion
//Record email as sent //I WILL NOT BE PROVIDING THIS PART
//#region RecordEmailInDB
That's just to send mails, I have many packages that build the emails to send. Most are variables to parameters on the call. The most complicated is the building of the Email Body and this is where your specific question comes into play.
This is a sample control flow:
There's a data flow that queries the details that need to be sent and are recorded into an object. As well as a record counter.
Back to control flow. There is a precendence constraint set to rowcount >0.
The I have a script task to build the body basically. And I have a class that converts the ADO Object into an HTML table.
public string BuildHTMLTablefromDataTable(DataTable t)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("<table border='1'><tr style='background-color: #1A5276; color:#FFFFFF;'>");
foreach (DataColumn c in t.Columns)
{
sb.Append("<th align='left'>");
sb.Append(c.ColumnName);
sb.Append("</th>");
}
sb.Append("</tr>");
int rc = 0;
foreach (DataRow r in t.Rows)
{
rc++;
//every other row switches from white to gray
string OpeningTR = "<tr style='background-color: " + ((rc % 2 == 1) ? "#E5E7E9;'>" : "#FCF3CF;'>");
sb.Append(OpeningTR);
foreach (DataColumn c in t.Columns)
{
sb.Append("<td align='left'>");
sb.Append(System.Web.HttpUtility.HtmlEncode(
r[c.ColumnName] == null ? String.Empty : r[c.ColumnName].ToString()
)); //This will handle any invalid characcters and convert null to empty string
sb.Append("</td>");
}
sb.Append("</tr>");
}
sb.Append("</table>");
return sb.ToString();
}
public string BuildBody(DataTable dt)
{
string body = "<P>The following are vouchers that are not in the voucher table but in the GL:</p>";
DataView v = new DataView(dt);
body += BuildHTMLTablefromDataTable(dt); //v.ToTable(true, "Name", "LastVisit", "DaysUntilTimeout", "ExpDate", "RoleName"));
return body;
}
public void Main()
{
#region Read Variables
System.Data.OleDb.OleDbDataAdapter da = new System.Data.OleDb.OleDbDataAdapter();
DataTable dt = new DataTable();
da.Fill(dt, Dts.Variables["User::Changes"].Value);
#endregion
string body = BuildBody(dt);
Dts.Variables["User::Body"].Value = body;
Dts.TaskResult = (int)ScriptResults.Success;
}
Finally I will call the SendMail package and pass the parameters.
For your purpose you will need to have a foreach around this package and adjust your where clause for the person on each pass.
This is an example of an email sent (Body only):

ContactList in RecyclerView with Cardview

i want to display my contacts in a recyclerview with cardelements. The problem is, that it is only displayed one contact for several times. So the same name is shown in every card. It seems like the cursor doesn't get to the next contact. But I don't know why. And I have to admit that I'm quite new to android.
Here is the code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts);
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
recList.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
contactRecAdapter ca = new contactRecAdapter(data);
recList.setAdapter(ca);
data = displayContact();
}
and my displayContact():
private ArrayList<contactInfo> displayContact(){
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);
String name ;
String number;
String id;
contactInfo cI = new contactInfo();
if(cur.getCount()>1){
while (cur.moveToNext()){
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if(Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)))>0){
cI.name = name;
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",new String[]{id},null);
while (pCur.moveToNext()){
number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
cI.number =number;
}
data.add(cI);
pCur.close();
}
}
}
cur.close();
return data;
}
Thank you!
What you need to understand is that currently, you have 1 contactInfo object and you are adding this contactInfo object into the data (arraylist).
Let's take it down one step at a time. This is what the code is currently doing:
Create a contactInfo object (Let's name it C1).
Set the name as "abc" and number as "123".
Add this C1 into data arraylist. --> Essentially the arraylist is holding a reference to where this C1 resides in the memory (i.e. it is only pointing to C1)
Cursor founds another contact and set the name of C1 to "def" and number to "456"
Add this C1 into data arraylist. (At this point of time, what the arraylist contains is actually 2 references, both pointing to C1)
Assuming the cursor finds nothing next and finally returns the data arraylist.
You will then get an arraylist that contains 2 records and both consists of contact name "def" and number "456".
Instead, what I would recommend based on your code is to simply add 1 more line to create a new contactInfo object each time you find a new contact, before setting the name.
private ArrayList<contactInfo> displayContact(){
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);
String name ;
String number;
String id;
contactInfo cI; //Remove the creation of cI object at the start
if(cur.getCount()>1){
while (cur.moveToNext()){
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if(Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)))>0){
cI = new contactInfo(); //Add in this to create a new cI object when there's a new contact
cI.name = name;
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",new String[]{id},null);
while (pCur.moveToNext()){
number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
cI.number =number;
}
data.add(cI);
pCur.close();
}
}
}
cur.close();
return data;
}
And you might want to optimize this code a little by having it fetch the phone numbers together with the names as the current way will take a fair bit of time if the data is huge. You can have a look at this post: https://stackoverflow.com/a/28690030/3717990

How do I write a test class for Messaging.SingleEmailMessage apex class?

I have written the below Apex class that processes incoming email that are sent to an email service address and creates a new task from the incoming mail and then associates this new task with a matching record in salesforce. The match is done on the record name and incoming email subject. The class also sends an email notifying the "Assigned to" user that they have received a reply on a request they are working on.
This works perfect in Sandbox but I have no experience writing test classes. Can anyone advise how I write the test class for the below?
global class RequestEmailHandler implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
String myPlainText = email.plainTextBody;
String subject = email.fromName + ' - ' + email.subject;
system.debug(email);
subject = subject.left(255);
Request__c request;
if (subject != null && subject.trim().length() > 0 && subject.indexOf('(R-') > 0) {
Integer idx = subject.indexOf('(R-');
String requestName = subject.substring(idx+1, subject.indexOf(')', idx));
request = [SELECT Id, Assigned_To__c FROM Request__c WHERE Name = :requestName];
}
if (request == null) {
result.message = 'We were unable to locate the associated request.This may be due to the unique "R" number being removed from the subject line.\n Please include the original email subject when replying to any emails.';
result.success = false;
return result;
}
// Add the email plain text into the local variable
Task task = new Task(
WhatId = request.Id,
Description = myPlainText,
Priority = 'Normal',
Status = 'Completed',
Type = 'Email',
Subject = subject,
ActivityDate = System.today(),
RecordTypeId = '01250000000HkEw');
insert task;
//Find the template
EmailTemplate theTemplate = [select id, name from EmailTemplate where DeveloperName = 'New_Email_Reply2'];
//Create a new email right after the task
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
//Add email To addresses to list
List<String> toAddresses = new List<String>();
toAddresses.add(email.fromAddress);
//Set the list of to addresses
mail.setToAddresses(toAddresses);
//Set the template id
mail.setTemplateId(theTemplate.id);
//The Id of the user
mail.setTargetObjectId(request.Assigned_To__c);
//Set the id of the request
mail.setWhatId(request.Id);
//If you need the email also saved as an activity, otherwise set to false
mail.setSaveAsActivity(false);
//Send Email
try {
Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
}
catch (EmailException e) {
system.debug('sendEmail error: ' + e.getMessage());
}
// Save attachments, if any
if (email.textAttachments != null)
{
for(Messaging.Inboundemail.TextAttachment tAttachment : email.textAttachments)
{
Attachment attachment = new Attachment();
attachment.Name = tAttachment.fileName;
attachment.Body = Blob.valueOf(tAttachment.body);
attachment.ParentId = request.Id;
insert attachment;
}
}
//Save any Binary Attachment
if (email.binaryAttachments != null)
{
for(Messaging.Inboundemail.BinaryAttachment bAttachment : email.binaryAttachments) {
Attachment attachment = new Attachment();
attachment.Name = bAttachment.fileName;
attachment.Body = bAttachment.body;
attachment.ParentId = request.Id;
insert attachment;
return result;
}
}
return result;
}
}
Below is my attempt which is only getting 24% coverage. I know it is missing vital code but I do not know enough about test classes to take it any further.
Can anyone assist?
Test Class
#isTest
public class testforemail {
static testMethod void insertRequest() {
Request__c requestToCreate = new Request__c();
requestToCreate.Subject__c= 'test';
requestToCreate.Requested_By_Email__c= 'graham.milne#fmr.com';
insert requestToCreate;
Messaging.InboundEmail email = new Messaging.InboundEmail();
Messaging.InboundEnvelope envelope = new Messaging.InboundEnvelope();
Request__c testRequest = [select Id,Name from Request__c limit 1];
System.debug(testRequest.Name);
email.subject = (testRequest.Name);
email.fromName = 'test test';
email.plainTextBody = 'Hello, this a test email body. for testing purposes only.Phone:123456 Bye';
Messaging.InboundEmail.BinaryAttachment[] binaryAttachments = new Messaging.InboundEmail.BinaryAttachment[1];
Messaging.InboundEmail.BinaryAttachment binaryAttachment = new Messaging.InboundEmail.BinaryAttachment();
binaryAttachment.Filename = 'test.txt';
String algorithmName = 'HMacSHA1';
Blob b = Crypto.generateMac(algorithmName, Blob.valueOf('test'),
Blob.valueOf('test_key'));
binaryAttachment.Body = b;
binaryAttachments[0] = binaryAttachment ;
email.binaryAttachments = binaryAttachments ;
envelope.fromAddress = 'user#fmr.com';
// Add the email plain text into the local variable
Task task = new Task(
WhatId = (testRequest.Id),
Description = email.plainTextBody,
Priority = 'Normal',
Status = 'Completed',
Type = 'Email',
Subject = (testRequest.Name),
ActivityDate = System.today(),
RecordTypeId = '01250000000HkEw');
insert task;
// setup controller object
RequestEmailHandler catcher = new RequestEmailHandler();
Messaging.InboundEmailResult result = catcher.handleInboundEmail(email, envelope);
System.assertEquals( true,result.success );
}
}
The first step is to identify what lines of code are not being covered by your test class.
If you're using Eclipse, you can see this from the Apex Test Runner View.
Or, you can see this from the Developer console as well.
Another thing you need to consider is the isolation of your DML operations in a separate utility class.
public class TestUtils
{
// create request objects method here
// create task objects method here
}
Also, check your debug logs and make sure your code is not throwing any exceptions (i.e., null pointer exceptions, DML exceptions, etc.)
You must also add assertions to check that your code is behaving as expected.
Hope this helps.
The main thing you need to do is to test as many use cases as you can via unit tests.
So, setup data for specific case and run you email processing. After email, check the result using System.assertEquals(). Make separate tests for each use case.
Then, if you don't hit at least 75%, check what is not covered. You, probably, either don't need that code (in case you covered all use cases), or don't write a test for use case, which uses those lines of code.

Excel doc contents to webservice

I have a wpf staff creation window in which I can create basic information like first name, last name etc this creates the staff in my REST web service. An example:
Client side:
private void CreateStaffMember_Click(object sender, RoutedEventArgs e)
{
string uri = "http://localhost:8001/Service/Staff";
StringBuilder sb = new StringBuilder();
sb.Append("<Staff>");
sb.AppendLine("<FirstName>" + this.textBox1.Text + "</FirstName>");
sb.AppendLine("<LastName>" + this.textBox2.Text + "</LastName>");
sb.AppendLine("<Password>" + this.passwordBox1.Password + "</Password>");
sb.AppendLine("</Staff>");
string NewStudent = sb.ToString();
byte[] arr = Encoding.UTF8.GetBytes(NewStudent);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Method = "POST";
req.ContentType = "application/xml";
req.ContentLength = arr.Length;
Stream reqStrm = req.GetRequestStream();
reqStrm.Write(arr, 0, arr.Length);
reqStrm.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
MessageBox.Show("Staff Creation: Status " + resp.StatusDescription);
reqStrm.Close();
resp.Close();
}
Web Service side:
#region POST
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/Staff")]
void AddStaff(Staff staff);
#endregion
public void AddStaff(Staff staff)
{
staff.StaffID = (++eCount).ToString();
staff.Salt = GenerateSalt();
byte[] passwordHash = Hash(staff.Password, staff.Salt);
staff.Password = Convert.ToBase64String(passwordHash);
staffmembers.Add(staff);
}
All fine on that side, but Im looking to "import" the staff details from an excel spreadsheet, not sure if import is the correct word but I want to take the first names and last names contained in such n such spreadsheet and add them to the web service from the client side wpf application.
How would I go about it? I have my open file dialog:
private void Import_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process open file dialog box results
if (result == true)
{
// Open document
string filename = dlg.FileName;
}
}
So I open my excel spread sheet then how would I go about taking the inner contents and sending it to the web service? Really stuck on the code or how to go about it :/
Just looking for an automated way of adding staff members rather than manually typing the names, but seeing as the staff excel doc could be named anything I wanted the open file dialog box. The structure inside will always be the same first name then last name.
First, here is my test Excel file that contains the Staff you want to import:
(Column 'A' if first name, column 'B' is last name and column 'C' is the password...)
Ok, so assuming that your code calling your web service works, here is my version of the Import_Click method (and a generic method to save new staff):
private void Import_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process open file dialog box results
if (result == true)
{
// Open document
string filename = dlg.FileName;
Microsoft.Office.Interop.Excel.Application vExcelObj = new Microsoft.Office.Interop.Excel.Application();
try
{
Workbook theWorkbook = vExcelObj.Workbooks.Open(filename, Type.Missing, true);
Worksheet sheet = theWorkbook.Worksheets[1]; // This is assuming that the list of staff is in the first worksheet
string vFirstName = "temp";
string vLastName = "temp";
string vPassword = "temp";
int vIndex = 1;
while (vFirstName != "")
{
// Change the letters of the appropriate columns here!
// In my example, 'A' is first name, 'B' is last name and 'C' is the password
vFirstName = sheet.get_Range("A" + vIndex.ToString()).Value.ToString();
vLastName = sheet.get_Range("B" + vIndex.ToString()).Value.ToString();
vPassword = sheet.get_Range("C" + vIndex.ToString()).Value.ToString();
this.SaveNewStaff(vFirstName, vLastName, vPassword);
vIndex++;
}
}
catch (Exception ex)
{
MessageBox.Show("Error processing excel file : " + ex.Message);
}
finally {
vExcelObj.Quit();
}
}
}
private void SaveNewStaff(string firstName, string lastName, string password) {
string uri = "http://localhost:8001/Service/Staff";
StringBuilder sb = new StringBuilder();
sb.Append("<Staff>");
sb.AppendLine("<FirstName>" + firstName + "</FirstName>");
sb.AppendLine("<LastName>" + lastName + "</LastName>");
sb.AppendLine("<Password>" + password + "</Password>");
sb.AppendLine("</Staff>");
string NewStudent = sb.ToString();
byte[] arr = Encoding.UTF8.GetBytes(NewStudent);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Method = "POST";
req.ContentType = "application/xml";
req.ContentLength = arr.Length;
Stream reqStrm = req.GetRequestStream();
reqStrm.Write(arr, 0, arr.Length);
reqStrm.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
//MessageBox.Show("Staff Creation: Status " + resp.StatusDescription);
reqStrm.Close();
resp.Close();
}
Note: I have REMed out the MessageBox in the call to the web service to make sure you are not annoyed by it if the list is long, but you are free to "unREM" it if you need confirmation for every staff creation. In the same line of taught, there is not validation that the creation has occurred successfully. I would need more details to create a decent validation process.
Also VERY important, this does not validate if the staff you are saving already exists in the list. If you re-run this import procedure multiple times, it may (and probably will) create duplicate entries.
Cheers

How to Use a Web Service to Update TextBox controls in ASP.NET?

I have a web form where I need to add, update, delete and read using a unique ID. So far I have managed to add, update and delete functions with little trouble.
However now I am having trouble getting my read function to work (understand I have a webform that has four text fields; ID, FIRSTNAME, SURNAME AND ADDRESS). Basically when an ID that has been previously created (using add button) is entered into the text field and the read button clicked it should update the other 3 text fields with the stored entries depending on the ID entered.
Here is my behind code (cs.) on the web form
protected void cmdRead_Click(object sender, EventArgs e)
{
// Create a reference to the Web service
DbWebService.WebService1 proxy = new DbWebService.WebService1();
// Create a person details object to send to the Web service.
string ADDRESS;
string SURNAME;
string FIRSTNAME;
string ID;
ADDRESS = txtAddress.Text;
SURNAME = txtSurname.Text;
FIRSTNAME = txtFirstname.Text;
ID = txtID.Text;
// Attempt to store in the Web service
bool rsp = proxy.ReadPerson(int.Parse(ID), FIRSTNAME, SURNAME, ADDRESS);
// Inform the user
if (rsp)
{
lblOutcome.Text = "Successfully read data.";
txtFirstname.Text = FIRSTNAME;
txtSurname.Text = SURNAME;
txtAddress.Text = ADDRESS;
}
else
{
lblOutcome.Text = "Failed to read data! Select a previously created ID!";
}
}
and here is my web function on the web service (which is where the SQL Server Express database is)
[WebMethod]
public bool ReadPerson(int ID, string FIRSTNAME, string SURNAME, string ADDRESS)
{
// In case of failure failure first
bool rtn = false;
// Connect to the Database
SqlConnection connection = new SqlConnection(#"Data Source=.\SQLEXPRESS;AttachDbFilename='|DataDirectory|\Database.mdf';Integrated Security=True;User Instance=True");
// Open the connection
connection.Open();
// Prepare an SQL Command
SqlCommand command = new SqlCommand(String.Format("SELECT FIRSTNAME, SURNAME, ADDRESS FROM PersonalDetails WHERE ID = '{0}'", ID), connection);
// Execute the SQL command and get a data reader.
SqlDataReader reader = command.ExecuteReader();
// Instruct the reader to read the first record.
if (reader.Read())
{
// A record exists, thus the return value is updated
FIRSTNAME = (string)reader["FIRSTNAME"];
SURNAME = (string)reader["SURNAME"];
ADDRESS = (string)reader["ADDRESS"];
rtn = true;
}
// Close the connection
connection.Close();
// Return the result.
return (rtn);
}
Now the problem is when I click read I get a success message (using a label as you can see in the behind code) but the fields don't update, I assume this is because of the (rtn = true;) statement. Therefore I thought something like this might work:
rtn = (bool)reader["ADDRESS"];
However with this I get a specified cast is not valid, so I figure maybe the bool doesn't work in this context, I think it might work if I use string instead but how do I convert, I think rtn needs a value in regards to the reader right??
Basically I am just looking for a solution to which will update the text fields in the web form.
There are several problems with your code. The most obvious is that your code cannot ever return the data from the database. You are sending FIRSTNAME etc. to the web service - you are not returning them from the web service.
There is no reason to have a bool return from the service to tell you whether or not it succeeded. Let the service throw an exception if it failed. Instead, you should return the fields from the database as the return of the service.
In the service:
public class Person
{
public string FIRSTNAME {get;set;}
public string SURNAME {get;set;}
public string ADDRESS {get;set;}
}
[WebMethod]
public Person ReadPerson(int ID)
{
// ...
if (reader.Read())
{
// A record exists, thus return the value
Person p = new Person();
p.FIRSTNAME = (string)reader["FIRSTNAME"];
p.SURNAME = (string)reader["SURNAME"];
p.ADDRESS = (string)reader["ADDRESS"];
rtn = p;
}
connection.Close();
return rtn;
}
Also, you should not be using a WebMethod or an ASMX web service unless you have no choice. ASMX is a legacy technology which is kept around only for backwards compatability. It should not be used for new development. You should use WCF instead.
The other issues with your code are resolved below:
[WebMethod]
public Person ReadPerson(int id)
{
using (
var connection =
new SqlConnection(
#"Data Source=.\SQLEXPRESS;
AttachDbFilename='|DataDirectory|\Database.mdf';
Integrated Security=True;
User Instance=True")
)
{
connection.Open();
using (
var command =
new SqlCommand(#"
SELECT FIRSTNAME, SURNAME, ADDRESS
FROM PersonalDetails
WHERE ID = #id",
connection))
{
var idParameter =
command.Parameters.Add(
"#id", SqlDbType.Int);
idParameter.Value = id;
using (var reader = command.ExecuteReader())
{
if (!reader.Read())
{
return null;
}
return new Person
{
Firstname =
(string)
reader["FIRSTNAME"],
Surname =
(string)
reader["SURNAME"],
Address =
(string)
reader["ADDRESS"]
};
}
}
}
}
The main issue is that the SqlConnection, SqlCommand, and SqlDataReader all need to be instantiated inside of using blocks. This ensures that the objects are disposed of (closed) whether or not an exception is thrown.
Next, you should not get into the habit of building queries through string manipulation; not even using String.Format. That leaves you open to "SQL Injection" attacks. Using parameters resolves that problem. See "Commands and Parameters " in MSDN.
One last minor issue: I recommend that you get out of the habit of placing comments on obvious statements. For instance, it's not necessary to comment that Open opens the connection to the database, or that return returns a value.

Resources