How to get the recipients of an email using EWS - c#

I am struggling to get the recipients of an email.
I understand that the Recipients is an array, so I need to put them into an array, but my code will not compile:
do
{
// set the prioperties we need for the entire result set
view.PropertySet = new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived,
ItemSchema.DisplayTo, EmailMessageSchema.ToRecipients,
EmailMessageSchema.From, EmailMessageSchema.IsRead,
EmailMessageSchema.HasAttachments, ItemSchema.MimeContent,
EmailMessageSchema.Body, EmailMessageSchema.Sender,
ItemSchema.Body) { RequestedBodyType = BodyType.Text };
// load the properties for the entire batch
service.LoadPropertiesForItems(results, view.PropertySet);
e2cSessionLog("\tcommon.GetUnReadMailAll", "retrieved " + results.Count() + " emails from Mailbox (" + common.strInboxURL + ")");
foreach (EmailMessage email in results)
// looping through all the emails
{
emailSenderName = email.From.Address;
sEmailSubject = email.Subject;
emailDateTimeReceived = email.DateTimeReceived.ToShortDateString();
emailHasAttachments = email.HasAttachments;
ItemId itemId = email.Id;
emailDisplayTo = email.DisplayTo;
sEmailBody = email.Body; //.Text;
Recipients = email.ToRecipients;
....
the last line there will not compile, as apparently I cannot implicitly convert the collection ToRecipients to a string...
so I tried to loop through all the ToRecipients:
string[] Recipients;
for (int iIdx=0; iIdx<-email.ToRecipients.Count; iIdx++)
{
Recipients[iIdx] = email.ToRecipients[iIdx].ToString();
}
but I have obviously not declare this properly, as it won't compile with the message that Recipients is unassigned.
What is the correct way to assign this?
I need to be able to use the recipients again later - for example to send them a 'heads up' email about a problem for example.

You need to initialize the array correctly, and you need to use the Address property of a ToRecipient:
var Recipients = new string[email.ToRecipients.Count];
for (int iIdx = 0; iIdx < email.ToRecipients.Count; iIdx++) {
Recipients[iIdx] = email.ToRecipients[iIdx].Address;
}
BTW, I think you have a typo in your pseudo-code:
for(...; iIdx<-email.ToRecipients.Count; ...) {
You have a minus - in there, which would result in no iterations since the first iteration would not pass (0 < -count is false). I think you mean
for(...; iIdx < email.ToRecipients.Count; ...) {
UPDATE
A much simpler, less error-prone, solution would be:
var recipients = email.ToRecipients
.Select(x => x.Address)
.ToList(); // or ToArray()

Related

Download attachments according to number in subject (MailKit library)

I'm downloading attachments from e-mail with this code:
int count = client.Count();
List<MimeMessage> allMessages = new List<MimeMessage>(count);
for (int i = 0; i < count; i++)
{
allMessages.Add(client.GetMessage(i));
foreach (var attachment in allMessages[i].Attachments)
{
using (var stream = File.Create(AppDomain.CurrentDomain.BaseDirectory + "/folderForSegments/" + attachment.ContentType.Name))
{
if (attachment is MessagePart)
{
var part = (MessagePart)attachment;
part.Message.WriteTo(stream);
}
else
{
var part = (MimePart)attachment;
part.ContentObject.DecodeTo(stream);
}
}
}
}
It works perfect but I want download attachments in sequence, according to number in subject. For example: if my inbox looks like this
attachments will be saved on my disc in order: 6, 8, 7, 3, 2... I want save attachments in order: 1, 2, 3, 4, 5... How can I do this?
For POP3, there's no way to download the messages in that order without knowing ahead of time what order the messages were in on the server.
If order is more important than wasted bandwidth, you could download the headers first using client.GetHeader(i) for each message so that you can use the Subject header value to determine the order, but that's a lot of wasted bandwidth because you'd just end up downloading the message headers a second time when you downloaded the messages.
Another option is to download all of the messages, add them to a List<T> and then sort them based on Subject before iterating over the messages and saving the attachments, but this might use too much RAM depending on how large your messages are.
Edit:
For IMAP, assuming your server supports the SORT extension, you can do something like this:
if (client.Capabilities.HasFlag (ImapCapabilities.Sort)) {
var query = SearchQuery.SubjectContains ("damian_mistrz_");
var orderBy = new OrderBy[] { OrderBy.Subject };
foreach (var uid in folder.Sort (query, orderBy) {
var message = folder.GetMessage (uid);
// save attachments...
}
}
If your server does not support SORT, then you could probably do something like this:
var query = SearchQuery.SubjectContains ("damian_mistrz_");
var orderBy = new OrderBy[] { OrderBy.Subject };
var uids = folder.Search (query);
var items = folder.Fetch (uids, MessageSummaryItems.Envelope | MessageSummaryItems.UniqueId);
items.Sort (orderBy);
foreach (var item in items) {
var message = folder.GetMessage (item.UniqueId);
// save the attachments...
}

Iterating whole list and updating string using ToLower - Not working

Technologies using.
C#
.NET 4.0
Visual Studio 2010
Problem.
I have a List<User> which contains an Email property. I want to lowercase all the email addresses within the list, but my implementation is not working. I'm using the following statement:
emails.ToList().ForEach(e => e.ToLower());
This didnt work at all for email addresses like Catherine.Burke#email.co.uk. I built the following to test this:
string email = "Catherine.Burke#email.co.uk";
email = email.ToLower();
Console.WriteLine("Email: " + email);
string email2 = "Catherine.Burke#email.co.uk";
string email3 = "Gareth.bradley#email.co.uk";
List<string> emails = new List<string>();
emails.Add(email2);
emails.Add(email3);
emails.ToList().ForEach(e => e.ToLower());
emails.ToList().ForEach(delegate(string e)
{
Console.WriteLine("ForEach deletegate : " + e);
});
List<EmailAddress> emailAddresses = new List<EmailAddress>();
emailAddresses.Add(new EmailAddress { FullAddress = "Catherine.Burke#email.co.uk" });
emailAddresses.Add(new EmailAddress { FullAddress = "Gareth.bradley#email.co.uk" });
emailAddresses.ToList().ForEach(e => e.FullAddress.ToLower());
emailAddresses.ToList().ForEach(delegate(EmailAddress e)
{
Console.WriteLine("EmailAddress delegate: " + e.FullAddress);
});
foreach (EmailAddress em in emailAddresses)
{
Console.WriteLine("Foreach Print: " + em.FullAddress);
}
Now I thought it might be the Culture and as these are names, it kept them uppercase, but when I used ToLower() on a singular string it worked. The above ran with the following output, as you can see the 1st line shows an email address with lowercase characters, whereas the implementation of the various List's I tried using ForEach() have not worked. I'm presuming my implementation of ForEach() is incorrect?
Making my comment an answer as requested:
Use a simple for-loop. List.ForEach is a method where you get the string as argument, you can't replace the whole reference there and since strings are immutable you can't change them either. You have to reassign the string returned from String.ToLower to your variable:
for(int i = 0; i < emails.Count; i++)
emails[i] = emails[i].ToLower();
Side-note: if you are making all emails lowercase to get a case-insensitive comparison it's better to use the String.Equals overload with the right StringComparison
string email1 = "Catherine.Burke#email.co.uk";
string email2 = "catherine.burke#email.co.uk";
if (String.Equals(email1, email2, StringComparison.InvariantCultureIgnoreCase))
{
// ...
}
emails.ToList().ForEach(e => e.ToLower()); does just call ToLower() but does not assign the result.
What you want is:
var lowerEmails = emails.Select(e => e.ToLower()).ToList();
Try this:
emailAddresses.ToList().ForEach(e => e.FullAddress = e.FullAdress.ToLower());
As weertzui altready mentions the ForEach-method simply calls the delegate. However the result of this action is not used in your code in any way.
However I´d strongly recommend to use a simply foreach:
foreach(var mail in emailadresses) mail.FullAdress = mail.FullAdress.ToLower();
which seems better readable to me.

LINQ results change at end of for loop

When performing a set of LINQ queries against a data-source (I'm using LINQ-to-SQL, but it happens here too using just a List<string> object), I end up getting a different result at the end of my checks.
Specifically, the code below is trying to find if a Fully Qualified Domain Name (FQDN) either exists in a list of host names (not all of which will be FQDNs or in the same domain, but the host identifier is what matters to me). The search is trying to find whether "host-6.domain.local" or any of its sub-components exist (i.e, "host-6.domain" or "host-6") in the list, which they do not. While inside the for-loop, we get the results we expect, but as soon as the for loop is finished, I get a result that has all of the contents of the list, which to me sounds like it is trying to find elements that match the empty string.
void MyMethod()
{
string fqdn = "host-6.domain.local";
string[] splitFqdn = fqdn.Split('.');
List<string> values = new List<string>();
values.add("host-1");
values.add("host-2.domain.local");
values.add("host-3.domain.local");
values.add("host-4");
values.add("host-5.other.local");
IEnumerable<string> queryResult = null;
for (int i = splitFqdn.Length; i > 0; i--)
{
result =
from value in values
where value.StartsWith(
string.Join(".", splitFqdn.Take(i)))
select value;
Console.WriteLine(
"Inside for loop, take " + i + ": " + result.Count());
}
Console.WriteLine();
Console.WriteLine(
"Outside for loop: " + result.Count());
}
Why is this happening and how can I get accurate results that I can still access after the for loop is finished?
You are getting bitten by LINQ's lazy execution and closure.
When you create an enumerable like you are doing here...
result =
from value in values
where value.StartsWith(
string.Join(".", splitFqdn.Take(i)))
select value;
It doesn't get evaluated until you do something that forces it to get evaluated... for instance when you do result.count()
Then later outside of your loop when you evaluate it again result.count() is evaluated with the last value of i that existed in your for loop which is not giving you what you want.
Try forcing evaluation by doing .ToList() on your enumerable like so... This code shows both values so you can compare.
void MyMethod()
{
string fqdn = "host-6.domain.local";
string[] splitFqdn = fqdn.Split('.');
List<string> values = new List<string>();
values.add("host-1");
values.add("host-2.domain.local");
values.add("host-3.domain.local");
values.add("host-4");
values.add("host-5.other.local");
IEnumerable<string> queryResult = null;
List<string> correctResult = null;
for (int i = splitFqdn.Length; i > 0; i--)
{
queryResult =
from value in values
where value.StartsWith(
string.Join(".", splitFqdn.Take(i)))
select value;
correctResult = queryResult.ToList();
Console.WriteLine(
"Inside for loop, take " + i + ": " + queryResult.Count());
}
Console.WriteLine();
Console.WriteLine(
"Outside for loop queryResult: " + queryResult.Count());
Console.WriteLine(
"Outside for loop correctResult: " + correctResult.Count());
}
EDIT: Thanks nlips for pointing out that I hadn't fully answered the question... and apologies for converting to method syntax but it would have taken longer to convert to query syntax.
void MyMethod()
{
string fqdn = "host-6.domain.local";
string[] splitFqdn = fqdn.Split('.');
List<string> values = new List<string>();
values.Add("host-1");
values.Add("host-2.domain.local");
values.Add("host-3.domain.local");
values.Add("host-4");
values.Add("host-5.other.local");
values.Add("host-5.other.local");
IEnumerable<string> queryResult = null;
List<string> correctResult = new List<string>();
for (int i = splitFqdn.Length; i > 0; i--)
{
correctResult = correctResult
.Union(values.Where(
value => value.StartsWith(string.Join(".", splitFqdn.Take(i)))))
.ToList();
}
}
I really like Kevin's answer to my question, but I wasn't a huge fan of calling .ToList() on the result since this would cause all of the objects that matched to be pulled from the database (eating up more memory) rather than executing a query that simply got the count of matching objects (which is a little faster and doesn't take the memory to store the objects), so using the information from his post, I have this additional solution that doesn't require pulling all objects from a database, and only runs a COUNT query (in the SQL sense).
To avoid the issue caused by capturing i which then becomes 0 at the end of the for-loop, I simply set up a temporary variable to hold the value I'm searching for.
void MyMethod()
{
string fqdn = "host-6.domain.local";
string[] splitFqdn = fqdn.Split('.');
List<string> values = new List<string>();
values.add("host-1");
values.add("host-2.domain.local");
values.add("host-3.domain.local");
values.add("host-4");
values.add("host-5.other.local");
IEnumerable<string> queryResult = null;
for (int i = splitFqdn.Length; i > 0; i--)
{
//taking the line referencing i out of the
//query expression prevents referencing i
//after it is set to 0 outside the for loop
string temp = string.Join(".", splitFqdn.Take(i));
//since temp isn't changed anywhere else, it won't
//get set to an invalid value after the loop exits
result =
from value in values
where value.StartsWith(temp)
select value;
Console.WriteLine(
"Inside for loop, take " + i + ": " + result.Count());
}
Console.WriteLine();
Console.WriteLine(
"Outside for loop: " + result.Count());
}
I think you need to call ToList when assigning to the result variable like this:
result =
(from value in values
where value.StartsWith(
string.Join(".", splitFqdn.Take(i)))
select value).ToList();

Method to check array list containing specific string

I have an ArrayList that import records from a database.
Is there any method to check whether the arrayList contains schname that i want to match to another list which is an api?
List<PrimaryClass> primaryList = new List<PrimaryClass>(e.Result);
PrimaryClass sc = new PrimaryClass();
foreach (string item in str)
{
for (int a = 0; a <= e.Result.Count - 1; a++)
{
string schname = e.Result.ElementAt(a).PrimarySchool;
string tophonour = e.Result.ElementAt(a).TopHonour;
string cca = e.Result.ElementAt(a).Cca;
string topstudent = e.Result.ElementAt(a).TopStudent;
string topaggregate = e.Result.ElementAt(a).TopAggregate;
string topimage = e.Result.ElementAt(a).TopImage;
if (item.Contains(schname))
{
}
}
}
This is what I have come up with so far, kindly correct any errors that I might have committed. Thanks.
How about ArrayList.Contains?
Try this
foreach( string row in arrayList){
if(row.contains(searchString)){
//put your code here.
}
}
Okay, now you've shown that it's actually a List<T>, it should be easy with LINQ:
if (primaryList.Any(x => item.Contains(x.PrimarySchool))
Note that you should really consider using foreach instead of a for loop to iterate over a list, unless you definitely need the index... and if you're dealing with a list, using the indexer is simpler than calling ElementAt.
// check all types
var containsAnyMatch = arrayList.Cast<object>().Any(arg => arg.ToString() == searchText);
// check strings only
var containsStringMatch = arrayList.OfType<string>().Any(arg => arg == searchText);

Using an Anonymous method to populate a property in an object initializer

Assuming sr is an IEnumerable<string>, I want to use code like this to do an inline calculation using two of the items from sr.Lines(). The problem is that the lambda is of type "lambda expression" and not a Decimal, which shares is expecting. Is there any way to do this type of inline method in an object initializer?
var trades =
from line in sr.Lines()
let items = line.Split('|')
select new Trade
{
Total = () => {
return Convert.ToDecimal(items[1]) + Convert.ToDecimal(items[2]);
},
Name = items[3]
}
You want a decimal expression, not a function:
var trades =
from line in sr.Lines()
let items = line.Split('|')
select new Trade
{
Total = Convert.ToDecimal(items[1]) + Convert.ToDecimal(items[2]),
Name = items[3]
};
Just in case someone else ends up here looking for a solution like I did.
Check How to call anonymous function in C#?.
var trades =
from line in sr.Lines()
let items = line.Split('|')
select new Trade
{
Total = ((Func<decimal>)(() =>
{
return Convert.ToDecimal(items[1]) + Convert.ToDecimal(items[2]);
}))(),
Name = items[3]
};

Categories