Laden...

Wie kann ich beim Abrufen von Exchange-Properties einen Status oder Ladebalken anzeigen?

Erstellt von SKB vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.658 Views
S
SKB Themenstarter:in
6 Beiträge seit 2020
vor 3 Jahren
Wie kann ich beim Abrufen von Exchange-Properties einen Status oder Ladebalken anzeigen?

Hallo,
ich lade per EWS einen ganzen Schwung an Mails (>3000) und lasse diese dann in einer e-Mail Liste ablegen, damit ich alle Mails nacheinander bearbeiten kann. Soweit so gut.

Da ich ja dann zu jeder Mail die Eigenschaften brauche, lade ich mit


exchange.LoadPropertiesForItems(emails, GetItemsPropertySet);

die Details vom Server. Leider dauert dies bei dem Haufen an Mails immer eine Weile. Hier würde ich gerne eine Statusmeldung einrichten, die zeigt, wie weit der Abruf schon ist. Ist sowas möglich?

Abholen der Mails sieht so aus:


private List<EmailMessage> CollectMailsAsync()
        {
            FolderId SharedMailbox = new FolderId(WellKnownFolderName.Inbox, g_id);
            SearchFilter searchFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, true));

            PropertySet FindItemPropertySet = new PropertySet(BasePropertySet.IdOnly);

            bool more = true;
            ItemView view = new ItemView(int.MaxValue, 0, OffsetBasePoint.Beginning);
            view.PropertySet = FindItemPropertySet;
            view.OrderBy.Add(EmailMessageSchema.DateTimeReceived, SortDirection.Ascending);
            
            FindItemsResults<Item> findResults;
            List<EmailMessage> emails = new List<EmailMessage>();
            
            while (more)
            {
                findResults = exchange.FindItems(SharedMailbox, searchFilter, view);

                foreach (var item in findResults.Items)
                {
                    emails.Add((EmailMessage)item);
                    
                }
                more = findResults.MoreAvailable;
                if (more)
                {
                    view.Offset += 1000;
                }
            }
            return emails;
        }

Funktion, um die Eigenschaften der Mails abzuholen:


        private async Task<List<EmailMessage>> PrepareMailsAsync(List<EmailMessage> emails)
        {
            PropertySet GetItemsPropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
            GetItemsPropertySet.RequestedBodyType = BodyType.Text;
            GetItemsPropertySet.BlockExternalImages = true;
            exchange.TraceEnabled = false;
            exchange.LoadPropertiesForItems(emails, GetItemsPropertySet);
            return emails;
        }

Funktion, um die Mails zu bearbeiten:


        public async void FetchNewMail(object sender, EventArgs e)
        {
            if (exchange != null)
            {
                try
                {
                    List<EmailMessage> emailMessages = null;
                    emailMessages = await System.Threading.Tasks.Task.Run(() => CollectMailsAsync());
                    emailMessages = await System.Threading.Tasks.Task.Run(() => PrepareMailsAsync(emailMessages));
                }
            }
        }

Vielen Dank für Eure Zeit!

T
2.219 Beiträge seit 2008
vor 3 Jahren

@SKB
Wozu machst du ein await und gleichzeitig Task.Run(), wenn deine Methode doch *Async heißen?
Eigentlich sollten diese einen Task zurückgeben, den du dann per await abarbeiten lässt.
So hast du Methoden, die synchron arbeiten, Async heißen und damit eigentlich andeuten, dass diese asynchron laufen.
Ebenfalls sollten Async Methoden, die Tasks liefern auch async markiert sein.

Da du die Menge an Emails anfangs nicht kennen kannst, wirst du erst eine generische Ladeanzeige anzeigen müssen z.B. mit der Informations, dass die Emails ermittelt werden.
Erst wenn du alle Emails hast, kennst du die Anzahl davon.
Jetzt würde ich jeweils gestaffelt für X Emails die Eigenschaften nachladen.
Damit kannst du dann auch eine entsprechende Anzeige umsetzen.

Müsste dann aber asynchron laufen, damit die UI nicht wieder blockiert.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

S
SKB Themenstarter:in
6 Beiträge seit 2020
vor 3 Jahren

Hallo Virus,
ich habe das await davor stehen, weil ich sonst einen Compiler Fehler erhalte.

Hast du einen Tipp, wie man dies besser abarbeiten könnte?

P
441 Beiträge seit 2014
vor 3 Jahren

Wie lautet denn dein Compiler Fehler?

Deine Methode "PrepareMailsAsync" heißt zwar Async und ist als Async markiert, ist aber nicht async und gibt vor allem keinen Task zurück. Sollte also in jedem Fall einen Compiler Fehler geben, weil der Rückgabetyp nicht stimmt.

S
SKB Themenstarter:in
6 Beiträge seit 2020
vor 3 Jahren

Angepasst habe ich die Tasks nun wie folgt:


private async Task<List<EmailMessage>> CollectMailsAsync()
        {
            FolderId SharedMailbox = new FolderId(WellKnownFolderName.Inbox, g_id);
            SearchFilter searchFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, true));

            PropertySet FindItemPropertySet = new PropertySet(BasePropertySet.IdOnly);

            bool more = true;
            ItemView view = new ItemView(int.MaxValue, 0, OffsetBasePoint.Beginning);
            view.PropertySet = FindItemPropertySet;
            view.OrderBy.Add(EmailMessageSchema.DateTimeReceived, SortDirection.Ascending);
            
            FindItemsResults<Item> findResults;
            List<EmailMessage> emails = new List<EmailMessage>();
            
            while (more)
            {
                findResults = exchange.FindItems(SharedMailbox, searchFilter, view);

                foreach (var item in findResults.Items)
                {
                    emails.Add((EmailMessage)item);
                    
                }
                more = findResults.MoreAvailable;
                if (more)
                {
                    view.Offset += 1000;
                }
            }
            return emails;
        }

        private async Task<List<EmailMessage>> PrepareMailsAsync(List<EmailMessage> emails)
        {
            PropertySet GetItemsPropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
            GetItemsPropertySet.RequestedBodyType = BodyType.Text;
            GetItemsPropertySet.BlockExternalImages = true;
            exchange.TraceEnabled = false;
            exchange.LoadPropertiesForItems(emails, GetItemsPropertySet);
            return emails;
        }


                    List<EmailMessage> emailMessages = null;
                    emailMessages = await System.Threading.Tasks.Task.Run(() => CollectMailsAsync());

                    // String for High Mail Load
                    string mail_message = emailMessages.Count() + " Emails wurden vom Server eingelesen ";

                    if (emailMessages.Count() > 1000)
                    {
                        mail_message += "und werden nun aufbereitet.\nDies kann einen Moment dauern ...";
                    } else
                    {
                        mail_message += "und werden nun aufbereitet ...";
                    }
                    lblStatus.Text = mail_message;
                    lblStatus.Refresh();
                    emailMessages = await System.Threading.Tasks.Task.Run(() => PrepareMailsAsync(emailMessages));

Meine GUI blockiert nicht und alle Mails werden sauber abgearbeitet. Jedoch fehlt mir noch die Idee, wie ich dort einen Status einbauen kann.

309 Beiträge seit 2020
vor 3 Jahren

Du hast eine Liste mit Mails, da weißt du die Anzahl.
Anstatt die zusammen in die Methode zu geben, kannst du sie ja einzeln abarbeiten und den Status aktualisieren.

Funktionieren deine Methoden wirklich?! Du gibst ja nicht mal einen Task zurück. Da macht der Aufruf dann halt auch keinen Sinn.


await System.Threading.Tasks.Task.Run(() => PrepareMailsAsync(emailMessages));

S
SKB Themenstarter:in
6 Beiträge seit 2020
vor 3 Jahren

Du hast eine Liste mit Mails, da weißt du die Anzahl.
Anstatt die zusammen in die Methode zu geben, kannst du sie ja einzeln abarbeiten und den Status aktualisieren.

Funktionieren deine Methoden wirklich?! Du gibst ja nicht mal einen Task zurück. Da macht der Aufruf dann halt auch keinen Sinn.

  
await System.Threading.Tasks.Task.Run(() => PrepareMailsAsync(emailMessages));  
  

Ja, sie funktionieren. Wenn ich die Mails einzeln abarbeite, muss ich ja alle 3000 Mails wieder einzeln an den Server schicken. So habe ich gedacht, das eine komplette Abfrage ok ist.

Kannst Du mir noch beantworten: Wieso muss ich denn einen Task zurückgeben?

Danke 😃

T
2.219 Beiträge seit 2008
vor 3 Jahren

@SKB
Weil Async Methoden immer einen Task/Value Task zurückgeben müssen.
Sonst deuten diese eine asynchrone Verarbeitung an, die sich nicht leisten.
Ist aber Teil der Programmierung mit Tasks über async/await.
Solltest du dir ggf. nochmal anschauen, wie es richtig gemacht wird.
Spätestens jetzt wo du in der UI arbeitest, solltest du die Tasks auch richtig anwenden.

Nachtrag:
Ich hatte oben auch schon den Hinweis gegeben, dass du deine Emails gestaffelt an den Server senden sollst.
Dadurch sparst du dir viele Roundtrips zwischen dir und dem Server und der Server muss nicht jede Email einzeln abfragen bzw. deren Eigenschaften.
Teste hier am besten aus, ab welcher Menge die Dauer zum abfragen der Eigenschaften noch kurz genug ist und dann hast du schon einen guten Ansatz.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.