An external process emails messages to an Exchange mailbox. I then have an Exchange Web Services (EWS) 2.0 application to fetch those email messages for subsequent processing. The EWS application is designed to fetch messages received on the current date, eg "received:today" in AQS parlance. The messages, however, are never retrieved - none are returned to the ItemView of the FindItems method:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
// sharedMailbox is a FolderId type set to a valid well-known location not reproduced here
FindItemsResults<Item> findResults = service.FindItems(sharedMailbox,"(subject:flotsam) AND (received:today)", new ItemView(20));
On a lark, changing this date to "received:yesterday" finally caught the desired messages, but an inspection of each message's explicit received date for each of the messages was not yesterday, but today:
Console.WriteLine(item.DateTimeReceived.ToLocalTime());
Console.WriteLine(item.DateTimeReceived.ToUniversalTime());
10/24/2016 1:05:38 AM
10/24/2016 6:05:38 AM
I suspected an oddity in the translation of the constants, and opted to provide explicit dates. However, explicitly defining the receipt date in "received:MM/DD/YYYY" form (rather than 'today' or 'yesterday') exhibited the same results:
FindItemsResults<Item> findResults = service.FindItems(sharedMailbox,"(subject:flotsam) AND (received:10/23/2016)", new ItemView(20));
Although the date is yesterday, this code did fetch messages retrieved today, which is 10/24/2016 (not 10/23/2016). Code specifying today, which should fetch the desired messages, actually fetched no messages:
FindItemsResults<Item> findResults = service.FindItems(sharedMailbox,"(subject:flotsam) AND (received:10/24/2016)", new ItemView(20));
In effect, the explicit dates are behaving exactly as the 'received:yesterday' and 'received:today' querystring values, so that implies these constants are simply being mapped to the values I hard-coded.
I then suspected timezone differences, or UTC conversion differences, but our local timezone is behind UTC, meaning that specifying "received:today" should, in reality, work in fetching messages received....today.
I am at a loss to know or understand why we are seeing this single-day discrepancy in fetched messages. What aspect of message date interpretation am I handling incorrectly?
EDIT: Per suggestion in comments, I modified the FindItems call to use a SearchFilter object, using a DateTime value of Now minus one day (actually, a value 24 hours from DateTime.Now. This created a datetime value that crossed into 10/23/2016, and retrieved the messages received today. So I cannot be sure that the filter really found the messages because they were received on 10/24, or because the search date range included yesterday (which caused the other searches to work):
SearchFilter sf = new SearchFilter.IsGreaterThan(ItemSchema.DateTimeReceived, new DateTime.Now.AddDays(-1));
Taking a cue from this, I modified the filter to use a DateTime without an explicit time element:
SearchFilter sf = new SearchFilter.IsGreaterThan(ItemSchema.DateTimeReceived, new DateTime(2016,10,24));
This search filter worked, returning both messages received today. This tends to suggest some semantic hiccup or uncertainty with the 'today' and 'yesterday' keywords.
I would prefer to find an AQS-based solution, if possible, or at least find a better understanding of why the querystring values aren't working as expected.
If you are using AQS with Exchange, you are allowed to use relational operators in the search query with date values and relative date keywords.
Although odd, the following expression worked the way you expected the relative date keyword "today" to work:
>Yesterday
Excerpted from "How to: Perform an AQS search by using EWS in Exchange"
"Date value types can also be compared with relational operators like greater than or less than, or specified as a range with the range operator ... For example, received:>11/30/2013, sent:>=yesterday, and received:12/1/2013..today are all valid query strings."
Related
I am trying fetch the whenChanged attribute for a user record from Active Directory using the DirectorySearcher class.
It seems the value is changed during or maybe after it is fetched because it is not the same as the value I can see in LDAP.
E.g. For my own profile the value in LDAP reads: 11/29/2022 5:10:21 Eastern Standard Time but after fetching this value through my code, it says 11/29/2022 10:10:24. Please note it is not 10:10 as per my system clock.
The obvious conclusion is that it is a time zone issue. But my system is in EST too. Also the difference in time is different for different users.
Does anyone know why this is happening?
There are a couple things going on.
As mentioned by Selvin, the value is always stored in GMT, which explains the 5-hour difference.
The value is not replicated between domain controllers, which explains the 3-second difference.
To explain the second point in more detail, let's take an example where a user's title is updated. When that update happens, the whenChanged attribute is updated on the DC where the change was made. Then when the new title value is replicated to another DC, that other DC updates the whenChanged attribute to the time when the replication happened. Because of that, the whenChanged value will be different on each DC.
The whenChanged values will usually be pretty close, but they can differ significantly. For example, when you logon, the lastLogon attribute is updated on the DC you authenticated to, along with whenChanged. However, lastLogon is not replicated, so the whenChanged value on all the other DCs will not get updated. That is, unless it's time to update the lastLogonTimestamp value, in which case that will replicate.
So if you're checking the value, make sure you're reading from the same DC. With DirectoryEntry, you can specify the DC you want to use in the LDAP path:
var user = new DirectoryEntry("LDAP://dc1.example.com/CN=someuser,OU=Users,DC=example,DC=com"
, null, null, AuthenticationTypes.ServerBind);
The use of AuthenticationTypes.ServerBind is because the documentation says:
Specifying a server name without also specifying this flag results in unnecessary network traffic.
Or you can get the DC that DirectoryEntry automatically found by using:
var dc = user.Options.GetCurrentServerName();
I'm having an application which keeps track of appointments in Exchange using the EWS API. Using the property .ItemId is bad, because though it's unique, it's likely to change (Exchange web services: why is ItemId not constant? [continued]). For tracking items, this is a bad situation. Therefore I use the property Appointment.ICalUid.
This property also doesn't always looks the same and as far I can see, it somehow changes. I've logged some changes. At first a record is created with the following .ICalUID:
5fc22493-7212-4c44-9cd6-971c3bae28af
Then the next time I look up the records in Exchange the same item returns another .ICalUID:
040000008200E00074C5B7101A82E00800000000F01883C1D49AD101000000000000000010000000C321E8A40C6DE948836C422E2DA8610C
Why do I have at first a string returned of 36 characters and later on a string of 190 characters long? Why is this value changing?
Edit:
That the short id is created when using an Android phone connected to the Exchange Server, and the long ID is created using Outlook on Windows 10 with Outlook 2013. But it changes somehow?
This is because the RFC for iCal doesn't define the format or length of the Uid https://www.rfc-editor.org/rfc/rfc5545 just that it has to be globally unique. That means its down to implementer eg some people use a guid, some use a guid and domain etc. Exchange/Outlook uses a GOID format in particular PidLidCleanGlobalObjectId https://msdn.microsoft.com/en-us/library/office/cc839502.aspx and PidLidGlobalObjectId https://msdn.microsoft.com/en-us/library/office/cc815676.aspx (which are typically generated by the Exchange server when the appointment is created) .
So you should expect different formats and I generally recommend you use the PidLidCleanGlobalObjectId extended property rather then the icaluid property because this will always returns consistent as once its set Exchange will never change this property where the strongly typed property can be inconsistent in certain cases like you seeing. (as a general rule it should return the GOID).
Cheers
Glen
We are doing Exchange web server synchronization with our application. To identify EWS changes we use; service.SyncFolderItems() method, like explain on MSDN. But, while doing initial sync it takes all the events in calendar, very older ones too. To avoid getting older events we need to use time period or Sync Start From time while requesting changes from SyncFolderItems() method.
1) Can SyncFolderItems() method accept user given time period when getting events from EWS ? & How ?
2) If not, Any workaround ?
There is a way to avoid older events in calendar using service.SyncFolderItems() method.
<SyncFolderItems>
<ItemShape/>
<SyncFolderId/>
<SyncState/>
<Ignore/>
<MaxChangesReturned/> <SyncScope/>
</SyncFolderItems>
That Ignore parameter will accept List of event Ids. and ignore them while syncing. To do that , First we need to retrieve older event IDs, Exchange will only accept two years old event
DateTime startDate = DateTime.Now.AddYears(-2); //start from two years earlier
DateTime endDate = DateTime.Now.AddMonths(-1); // End One Month before,
//you can use Convert.ToDateTime("01/01/2013"); what ever date you wanted.
Create Item id List;
List<ItemId> itmid = new List<ItemId>();
Create Calendar View object;
CalendarView cView = new CalendarView(startDate, endDate);
Retrieve Appointments;
// Retrieve a collection of appointments by using the calendar view.
FindItemsResults<Item> appointments = service.FindItems(WellKnownFolderName.Calendar, cView);
Or you can use this, But previous code have some optimization. (Google)
FindItemsResults<Appointment> appointments = service.FindAppointments(WellKnownFolderName.Calendar, cView);
Add retrieve event ids into list,
foreach (var item in appointments)
{
itmid.Add(item.Id);
}
Finally, in your SyncFolderItems method will looks like this;
service.SyncFolderItems(new FolderId(WellKnownFolderName.Calendar), PropertySet.IdOnly, itmid, 10, SyncFolderItemsScope.NormalItems, sSyncState);
Hope this will help any of you.
Currently, SyncFolderItems only supports synchronizing the entire mailbox. It doesn't support synchronizing starting from a specific time period. This type of request has been shared with the product planners. Hopefully we'll see this type of functionality.
In terms of workarounds, you could:
1) Sync all of the events with only the ItemId. Throw away items don't need.
2) Perform a FindItems with your intended time period, use GetItem (Bind) to get the events, and then use notifications to learn when a new item arrives, or when an item is updated. What you won't get with this is what has changed. For new items, this isn't an issue. But for updated items, you'll have to perform a GetItem (Load) and then diff the updated and old items to see what has changed.
I have a linq-to-sql query that groups results by DateTime, something like this:
var TheQuery = from ....
where ....
group a by a.TheDateTime.Date in TheGroups
The problem is that this groups by actual dates that are stored in UTC in the DB. Let's say that I want to group by dates based on the user's timezone. So for instance, if he's in the PST timezone, group by "UTC minus 8 hours", or whatever other offset.
Thanks for your suggestions.
If you're getting all the information back anyway, I'd frankly bring it back to the client and do the grouping in LINQ to Objects. That way you can concentrate on using one platform's date and time API - it's hard enough using one API correctly when it comes to time zones, let alone two.
(As an aside and a plug, it also means you can use whatever .NET API you like for this - such as Noda Time ;)
So I'd put all the filtering and projection you can do before the grouping, then use AsEnumerable() to force the rest of the execution to occur locally.
Don't forget that you almost certainly can't just add or subtract 8 hours, unless the user is really in a time zone which doesn't use daylight saving time (and hasn't historically). Are you also sure that it's appropriate to apply the same time zone to all the data? (It may well - just checking.)
We have a new application in the works where we use streaming subscriptions to read emails as they come to our mailbox. Since we would like to read all past emails (roughly 50,000) we need some way of manually finding them instead of relying on the on notification event provided by EWS. The plan is to serialize the list of emails we read and parse for future database testing scenarios. I originally posted this question on StackOverflow asking how to retrieve all emails from a mailbox regardless of which folder they're in. Essentially, the mailbox I'm after organizes emails by year, month and day. Issuing a FindFolders() operation yields around 1,300 folders. I would rather not have to issue a FindItems() operation for every folder as it would take hours upon hours to read every email. Henning Krause mentioned that the managed API does not support querying Exchange for emails that are in a given list of parent folder ids. Instead I would have to use the FindItem operation. After some testing around I was able to retrieve my limit of items (1,000). Since the FindItem operation doesn't outright return if there are more items for your query I had to simply check that if the FindItem operation return 1,000 items then it was safe to query again until the result count is less than 1,000. Each loop through I increment my offset by 1,000.
When my offset is set to 0 I get the first 1,000 emails with no issues. On the second iteration I set my offset to 1,000 and receive no items, yet the operation was a success. Here is what I receive when setting an offset. If it matters I'm offsetting from the beginning. Below you can see one of the many FindItemResponseMessage nodes that clearly show there are 29 items in the view and that they all shown.
<m:FindItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:RootFolder IndexedPagingOffset="29" TotalItemsInView="29" IncludesLastItemInRange="true">
<t:Items />
</m:RootFolder>
</m:FindItemResponseMessage>
What am I missing?
Thank you.