C# MailKit get messages conversation/replies inside treeview - c#

i'm trying to replicate the view of a mailbox, i try to use references and threads but don't work, somethimes thread has uniqueid null.
foreach (var rfr in Message.References ?? new MimeKit.MessageIdList())
{
var _uids = Imap.Inbox.Search(SearchQuery.HeaderContains("Message-Id", rfr));
if (_uids.Count > 0)
{
var _messages = Imap.Inbox.Fetch(_uids.ToList(), MessageSummaryItems.Envelope | MessageSummaryItems.Flags).OrderByDescending(o => o.Date);
foreach (var msg in _messages)
{
_Added.Add(msg.UniqueId);
RequestModel _model = new RequestModel
{
Address = msg.Envelope.From.Mailboxes.FirstOrDefault().Name ?? msg.Envelope.From.Mailboxes.FirstOrDefault().Address,
Subject = msg.Envelope.Subject,
Date = msg.Date.ToLocalTime().ToString(),
IsSeen = msg.Flags.Value.HasFlag(MailKit.MessageFlags.Seen),
Childs = new List<Scratch.MainWindow.RequestModel>(),
};
_retValue.Add(_model);
}
}
}
var _messages = _imapClient.Inbox.Fetch(_uids.ToList(), MessageSummaryItems.Envelope | MessageSummaryItems.Flags | MessageSummaryItems.References).OrderByDescending(o => o.Date).Take(50);
var _threads = MessageThreader.Thread(_messages, ThreadingAlgorithm.References);

The MessageThreader class uses the References (which contin a list of Message-Id values tracing back to the root of the thread) in order to construct the tree of messages. Obviously, if the list of message summaries that you give to the MessageThreader are missing some of those references, then the returned tree will have some empty nodes. This is why some of said nodes have a null UniqueId value.
FWIW, a few tips for you:
Don't do _uids.ToList() - _uids is already an IList<UniqueId>, why duplicate it for no reason?
It's more efficient to use the orderBy argument to MessageThreader.
Like this:
var orderBy = new OrderBy[] { OrderBy.ReverseDate };
var threads = MessageThreader.Thread (summaries, ThreadingAlgorithm.References, orderBy);

Related

Returning list basic c sharp

I am new to programming and been tasked to grab data from an API.
The issue I have is basic C# returning the contents of brakeparts. I'm passing the part id and obtaining a list of brake parts correctly. I need to assign the partid to the list, which seems to work when adding a breakpoint, however how do I return this list? Return brake parts doesn't work. They could be potentially many parts it returns whilst looping I need it to append to the Parts list. I know its probably something quite simple but I can't get my head around returning back the whole list. I've tried brakeparts.AddRange(brakeparts); This doesn't work too.
private static List<Parts> getBrakeparts(List<int> partids)
{
var restClient = new RestClient("https://example.example.net/api");
foreach (var partid in partids)
{
var returnreq = new RestRequest($"/brakeparts/{partid}/ ", Method.GET);
var res = restClient.Execute(returnreq);
Parts brakeparts = JsonConvert.DeserializeObject<Parts>(res.Content);
brakeparts.PartID = partid;
}
return brakeparts;
}
As I understand, you're simply trying to return a list of parts which you want to fill with the Parts breakparts that you deserialized from the Json response.
Simply create a List<Parts> instance and add the breakparts to it in the loop.
It could look like the following.
private static List<Parts> getBrakeparts(List<int> partids)
{
var restClient = new RestClient("https://example.example.net/api");
List<Parts> parts = new List<Parts>();
foreach (var partid in partids)
{
var returnreq = new RestRequest($"/brakeparts/{partid}/ ", Method.GET);
var res = restClient.Execute(returnreq);
Parts brakeparts = JsonConvert.DeserializeObject<Parts>(res.Content);
brakeparts.PartID = partid;
parts.Add(brakeparts);
}
return parts;
}

How to create a nested (parent-Child ) JSON response in c#?

I am making a web service which will give a response in JSON format. I have fetched data from sql server and stored it in a Datatable. This is how the dt looks:-
id Caption pid
F182 GLOBAL REPORTS NULL
F184 software NULL
F1227 LYB P&L Reports F184
F1245 LYB Training F184
F1239 test3 F182
F1249 Paavan_Test_Reports F184
Items in caption column which have pid as Null are the parents and they have children which have the same pid as their respective parent's id.
Eg: GLOBAL REPORTS has 1 child i.e test3 and software has 3 child.
I want the JSON response into the following format
[{
id='F182',
caption='GLOBAL REPORTS',
pid=null;
items:[{
id='F1239',
caption='test3',
pid='F182'}]
},
{
id='F184',
caption='software',
pid='NULL',
items:[{
id='F1227',
caption='LYB P&L Reports',
pid='F184'
},
{
id='F1245',
caption='LYB Training',
pid='F184'
},
{
id='F1249',
caption='Paavan_Test_Reports',
pid='F184'
}
}]
i have made a class folder
class folder
{
string id{get;set;}
string pid{get;set;}
string caption{get;set;}
}
How can i get the array of objects items which will contain all the available child of a particular parent? I am new to Json objects and responses,
I have tried using the following method:
var obj = dt.AsEnumerable()
.GroupBy(r => r["pid"])
.ToDictionary(g => g.Key.ToString(),
g => g.Select(r => new {
item = r["caption"].ToString(),
}).ToArray());
var json = JsonConvert.SerializeObject(obj);
but this is giving me a simple json response without any hierarchy.
You need to build object hierarchy before serialization. To do so define a new property in your folder class:
public IEnumerable<folder> items { get; set; }
Now you can recursively search childs for each root folder:
public static IEnumerable<folder> BuildTree(folder current, folder[] allItems)
{
var childs = allItems.Where(c => c.pid == current.id).ToArray();
foreach (var child in childs)
child.items = BuildTree(child, allItems);
current.items = childs;
return childs;
}
Usage:
var input = new[] // dt.AsEnumerable() in your case
{
new folder {id = "F182", caption = "GLOBAL REPORTS", pid = null },
new folder {id = "F184", caption = "software", pid = null },
new folder {id = "F1227", caption = "LYB P&L Reports", pid = "F184" },
new folder {id = "F1245", caption = "LYB Training", pid = "F184" },
new folder {id = "F1239", caption = "test3", pid = "F182" },
new folder {id = "F1249", caption = "Paavan_Test_Reports", pid = "F184" },
};
var roots = input.Where(i => i.pid == null);
foreach (var root in roots)
BuildTree(root, input);
var json = JsonConvert.SerializeObject(roots, Formatting.Indented);
Also, if you want to hide empty items you can define method ShouldSerialize in folder class:
public bool ShouldSerializeitems()
{
return items.Any();
}
Demo is here
I think what you want is to have a list of folders inside the folder, like the following:
class folder
{
string id{get;set;}
string pid{get;set;}
string caption{get;set;}
List<folder> items {get;set;}
}
When you use JSONConvert in this case, you can run into having circular dependencies, if a parent exists in the child's collection. You can use a SerializerSettings object to ignore these cases, and for children in the items list, you can set their collection to null, so it won't serialized.
However, the desired output you specify also lists an array of folders. So in the end, you'll need to serialize a collection(List or array for example) of the folder class. Something like.
With regards to how you would load in the data, I imagine you've already loaded the folders with id, pid and caption, but the items property is empty.
In order to establish the parent/child relation, you could so something like
var folders = dt.AsEnumerable().ToList();
foreach(var folder in folders) {
folder.items = folders.Where(f => f.pid.Equals(folder.id)).ToList();
}
var parentFolders = folders.Where(f => f.pid is null);
var json = JsonConvert.SerializeObject(parentFolders, new SerializerSettings () {
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

Why this difference between foreach vs Parallel.ForEach?

Can anyone explain to me in simple langauage why I get a file about 65 k when using foreach and more then 3 GB when using Parallel.ForEach?
The code for the foreach:
// start node xml document
var logItems = new XElement("log", new XAttribute("start", DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss")));
var products = new ProductLogic().SelectProducts();
var productGroupLogic = new ProductGroupLogic();
var productOptionLogic = new ProductOptionLogic();
// loop through all products
foreach (var product in products)
{
// is in a specific group
var id = Convert.ToInt32(product["ProductID"]);
var isInGroup = productGroupLogic.GetProductGroups(new int[] { id }.ToList(), groupId).Count > 0;
// get product stock per option
var productSizes = productOptionLogic.GetProductStockByProductId(id).ToList();
// any stock available
var stock = productSizes.Sum(ps => ps.Stock);
var hasStock = stock > 0;
// get webpage for this product
var productUrl = string.Format(url, id);
var htmlPage = Html.Page.GetWebPage(productUrl);
// check if there is anything to log
var addToLog = false;
XElement sizeElements = null;
// if has no stock or in group
if (!hasStock || isInGroupNew)
{
// page shows => not ok => LOG!
if (!htmlPage.NotFound) addToLog = true;
}
// if page is ok
if (htmlPage.IsOk)
{
sizeElements = GetSizeElements(htmlPage.Html, productSizes);
addToLog = sizeElements != null;
}
if (addToLog) logItems.Add(CreateElement(productUrl, htmlPage, stock, isInGroup, sizeElements));
}
// save
var xDocument = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("log", logItems));
xDocument.Save(fileName);
Use of the parallel code is a minor change, just replaced the foreach with Parallel.ForEach:
// loop through all products
Parallel.ForEach(products, product =>
{
... code ...
};
The methods GetSizeElements and CreateElements are both static.
update1
I made the methods GetSizeElements and CreateElements threadsafe with a lock, also doesn't help.
update2
I get answer to solve the problem. That's nice and fine. But I would like to get some more insigths on why this codes creates a file that is so much bigger then the foreach solutions. I am trying get some more sense in how the code is working when using threads. That way I get more insight and can I learn to avoid the pitfalls.
One thing stands out:
if (addToLog)
logItems.Add(CreateElement(productUrl, htmlPage, stock, isInGroup, sizeElements));
logItems is not tread-safe. That could be your core problem but there are lots of other possibilities.
You have the output files, look for the differences.
Try to define the following parameters inside the foreach loop.
var productGroupLogic = new ProductGroupLogic();
var productOptionLogic = new ProductOptionLogic();
I think the only two is used by all of your threads inside the parallel foreach loop and the result is multiplied unnecessaryly.

LINQ and creating NON anonymous return values

I think I understand returning records of an anonymous type from But in this I want to create NEW CatalogEntries, and set them from the values selected. (context is a Devart LinqConnect database context, which lets me grab a view).
My solution works, but it seems clumsy. I want to do this in one from statement.
var query = from it in context.Viewbostons
select it;
foreach (GPLContext.Viewboston item in query)
{
CatalogEntry card = new CatalogEntry();
card.idx = item.Idx;
card.product = item.Product;
card.size = (long)item.SizeBytes;
card.date = item.Date.ToString();
card.type = item.Type;
card.classification = item.Classification;
card.distributor = item.Distributor;
card.egplDate = item.EgplDate.ToString();
card.classificationVal = (int)item.ClassificationInt;
card.handling = item.Handling;
card.creator = item.Creator;
card.datum = item.Datum;
card.elevation = (int)item.ElevationFt;
card.description = item.Description;
card.dirLocation = item.DoLocation;
card.bbox = item.Bbox;
card.uniqID = item.UniqId;
values.Add(card);
}
CatalogResults response = new CatalogResults();
I just tried this:
var query2 = from item in context.Viewbostons
select new CatalogResults
{ item.Idx,
item.Product,
(long)item.SizeBytes,
item.Date.ToString(),
item.Type,
item.Classification,
item.Distributor,
item.EgplDate.ToString(),
(int)item.ClassificationInt,
item.Handling,
item.Creator,
item.Datum,
(int)item.ElevationFt,
item.Description,
item.DoLocation,
item.Bbox,
item.UniqId
};
But I get the following error:
Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a
collection initializer because it does not implement
'System.Collections.IEnumerable' C:\Users\ysg4206\Documents\Visual
Studio
2010\Projects\CatalogService\CatalogService\CatalogService.svc.cs 91 25 CatalogService
I should tell you what the definition of the CatalogResults is that I want to return:
[DataContract]
public class CatalogResults
{
CatalogEntry[] _results;
[DataMember]
public CatalogEntry[] results
{
get { return _results; }
set { _results = value; }
}
}
My mind is dull today, apologies to all. You are being helpful. The end result is going to be serialized by WCF to a JSON structure, I need the array wrapped in a object with some information about size, etc.
Since .NET 3.0 you can use object initializer like shown below:
var catalogResults = new CatalogResults
{
results = context.Viewbostons
.Select(it => new CatalogEntry
{
idx = it.Idx,
product = it.Product,
...
})
.ToArray()
};
So if this is only one place where you are using CatalogEntry property setters - make all properties read-only so CatalogEntry will be immutable.
MSDN, Object initializer:
Object initializers let you assign values to any accessible fields or properties of an
object at creation time without having to explicitly invoke a constructor.
The trick here is to create a IQueryable, and then take the FirstOrDefault() value as your response (if you want a single response) or ToArray() (if you want an array). The error you are getting (Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a collection initializer because it does not implement 'System.Collections.IEnumerable') is because you're trying to create an IEnumerable within the CatalogEntry object (by referencing the item variable).
var response = (from item in context.Viewbostons
select new CatalogEntry()
{
idx = item.Idx,
product = item.Product,
size = (long)item.SizeBytes,
...
}).ToArray();
You don't have to create anonymous types in a Linq select. You can specify your real type.
var query = context.Viewbostons.Select( it =>
new CatalogEntry
{
idx = it.idx,
... etc
});
This should work:
var query = from it in context.Viewbostons
select new CatalogEntry()
{
// ...
};

Errors when creating a custom Querable object with MVC and Subsonic pagedlist

hiya, i have the following code but when i try and create a new IQuerable i get an error that the interface cannot be implemented, if i take away the new i get a not implemented exception, have had to jump back and work on some old ASP classic sites for past month and for the life of me i can not wake my brain up into C# mode.
Could you please have a look at below and give me some clues on where i'm going wrong:
The code is to create a list of priceItems, but instead of a categoryID (int) i am going to be showing the name as string.
public ActionResult ViewPriceItems(int? page)
{
var crm = 0;
page = GetPage(page);
// try and create items2
IQueryable<ViewPriceItemsModel> items2 = new IQueryable<ViewPriceItemsModel>();
// the data to be paged,but unmodified
var olditems = PriceItem.All().OrderBy(x => x.PriceItemID);
foreach (var item in olditems)
{
// set category as the name not the ID for easier reading
items2.Concat(new [] {new ViewPriceItemsModel {ID = item.PriceItemID,
Name = item.PriceItem_Name,
Category = PriceCategory.SingleOrDefault(
x => x.PriceCategoryID == item.PriceItem_PriceCategory_ID).PriceCategory_Name,
Display = item.PriceItems_DisplayMethod}});
}
crm = olditems.Count() / MaxResultsPerPage;
ViewData["numtpages"] = crm;
ViewData["curtpage"] = page + 1;
// return a paged result set
return View(new PagedList<ViewPriceItemsModel>(items2, page ?? 0, MaxResultsPerPage));
}
many thanks
you do not need to create items2. remove the line with comment try and create items2. Use the following code. I have not tested this. But I hope this works.
var items2 = (from item in olditems
select new ViewPriceItemsModel
{
ID = item.PriceItemID,
Name = item.PriceItem_Name,
Category = PriceCategory.SingleOrDefault(
x => x.PriceCategoryID == item.PriceItem_PriceCategory_ID).PriceCategory_Name,
Display = item.PriceItems_DisplayMethod
}).AsQueryable();

Categories