I have the following method that is supposed to parse information from an XML response and return a collection of users.
I've opted into creating a Friend class and returning a List<Friend> to the calling method.
Here's what I have so far, but I noticed that the ids.ToList().Count method parses every single id element to a List, then does it again in the for conditional. It's just super ineffective.
public List<Friend> FindFriends()
{
List<Friend> friendList = new List<Friend>();
var friends = doc.Element("ipb").Element("profile").Element("friends").Elements("user");
var ids = from fr in friends
select fr.Element("id").Value;
var names = from fr in friends
select fr.Element("name").Value;
var urls = from fr in friends
select fr.Element("url").Value;
var photos = from fr in friends
select fr.Element("photo").Value;
if (ids.ToList().Count > 0)
{
for (int i = 0; i < ids.ToList().Count; i++)
{
Friend buddy = new Friend();
buddy.ID = ids.ToList()[i];
buddy.Name = names.ToList()[i];
buddy.URL = urls.ToList()[i];
buddy.Photo = photos.ToList()[i];
friendList.Add(buddy);
}
}
return friendList;
}
First question - do you have to return a List<Friend>? Can you return an IEnumerable<Friend> instead? If so, performance gets a lot better:
IEnumerable<Friend> FindFriends()
{
return doc.Descendants("user").Select(user => new Friend {
ID = user.Element("id").Value,
Name = user.Element("name").Value,
Url = user.Element("url").Value,
Photo = user.Element("photo").Value
});
}
Rather than actually creating new buckets and stuffing values into them, this creates a projection, or a new object that simply contains all of the logic for how to create the new Friend objects without actually creating them. They get created when the caller eventually starts to foreach over the IEnumerable. This is called "deferred execution".
This also makes one assumption - All the <user> nodes in your XML fragment are friends. If that isn't true, the first part of the XML selection might need to be a little more complex.
And as #anon points out, even if you do need to return a List<Friend> for some reason not obvious from the information you've provided, you can just call .ToList() at the end of the return statement. This will just execute the projection I described above straight into a new bucket, so you only ever create one.
Why do you need the separate ids/names/urls/photos variables? Combine it all. You can eliminate the ToList() call if you don't need a List.
List<Friend> friendList = (from f in doc.Element("ipb").Element("profile").Element("friends").Elements("user")
select new Friend() {
ID = f.Element("id").Value,
Name = f.Element("name").Value,
URL = f.Element("url").Value,
Photo = f.Element("photo").Value
}).ToList();
return friendList;
Related
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;
}
I'm having some issues construction a LINQ query to find MATCHING values in two IEnumerables I have from CSV files and outputting those matching values to another list for some bookkeeping later on in my application.
My classes for the IEnumerables and the related code (using CSVHelper) to read the CSVs into the IEnumerables are below. Any input on where to begin, LINQ query wise, to find those matching values and output to a list? I'm relatively new to LINQ (usually use SQL in the backend) and I'm finding it a bit difficult to do exactly what I want to.
CLASSES:
class StudentSuccessStudents
{
[CsvColumn(Name ="StudentID", FieldIndex = 1)]
public string StudentID { get; set; }
}
class PlacementStudents
{
[CsvColumn(Name = "StudentId", FieldIndex = 1)]
public string StudentId { get; set; }
}
PROGRAM:
CsvFileDescription inputCsvStuSuccess = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true,
EnforceCsvColumnAttribute = false
};
CsvContext ccStuSuccess = new CsvContext();
CsvFileDescription inputCsvStuScores = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = false,
EnforceCsvColumnAttribute = true
};
CsvContext ccStuScores = new CsvContext();
IEnumerable<StudentSuccessStudents> students = ccStuSuccess.Read<StudentSuccessStudents>(filePath, inputCsvStuSuccess);
IEnumerable<PlacementStudents> outputStudents = ccStuScores.Read<PlacementStudents>(csvPath, inputCsvStuScores);
Any suggestions on how to get all my "StudentID" fields in the first list that match a "StudentId" in the second one to output to another list with LINQ? I basically need to have that "matching" list so I can safely ignore those values elsewhere.
You could always use the SQL syntax of Linq like shown below. This way it looks more like something you're use to, and gets you the duplicate values you need. Also looks more readable (in my opinion) too.
var duplicates = from success in students
join placement in outputStudents on success.StudentID equals placement.StudentId
select success.StudentID;
This is a linq operator to do this functionality called Intersect so
If they were both the same type you could do this
var result = students.Intersect(outputStudents);
Which should be the fastest way.
Since they are different types you do this
var result = students.Intersect(outputStudents.Select(x => new StudentSuccessStudent(x.id) );
basically you create a new list of the correct type dynamically
This is an example where inheritance or interfaces are powerful. If they both inherited from the same type then you could intersect on that type and C# would solve this problem super fast.
I will attempt to be as specific as possible. So we are using Sitefinity 8.1.5800, I have a couple dynamic content modules named ReleaseNotes and ReleaseNoteItems. ReleaseNotes has some fields but no reference to ReleaseNoteItems.
Release Note Items has fields and a related data field to ReleaseNotes.
So I can query all ReleaseNoteItems as dynamic content pretty quickly less than a second.
I then use these objects provided by sitefinity and map them to a C# object so I can use strong type. This mapping process is taking almost a minute and using over 600 queries for only 322 items (N+1).
In Short: I need to get all sitefinity objects and Map them to a usable c# object quicker than I currently am.
The method for fetching the dynamic content items (takes milliseconds):
private IList<DynamicContent> GetAllLiveReleaseNoteItemsByReleaseNoteParentId(Guid releaseNoteParentId)
{
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(String.Empty);
Type releasenoteitemType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ReleaseNoteItems.Releasenoteitem");
string releaseNoteParentTypeString = "Telerik.Sitefinity.DynamicTypes.Model.ReleaseNotes.Releasenote";
var provider = dynamicModuleManager.Provider as OpenAccessDynamicModuleProvider;
int? totalCount = 0;
var cultureName = "en";
Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName);
Type releasenoteType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ReleaseNotes.Releasenote");
// This is how we get the releasenote items through filtering
DynamicContent myCurrentItem = dynamicModuleManager.GetDataItem(releasenoteType, releaseNoteParentId);
var myMasterParent =
dynamicModuleManager.Lifecycle.GetMaster(myCurrentItem) as DynamicContent;
var relatingItems = provider.GetRelatedItems(
releaseNoteParentTypeString,
"OpenAccessProvider",
myMasterParent.Id,
string.Empty,
releasenoteitemType,
ContentLifecycleStatus.Live,
string.Empty,
string.Empty,
null,
null,
ref totalCount,
RelationDirection.Parent).OfType<DynamicContent>();
IList<DynamicContent> allReleaseNoteItems = relatingItems.ToList();
return allReleaseNoteItems;
}
This is the method that takes almost a minute that is mapping sitefinity object to C# object:
public IList<ReleaseNoteItemModel> GetReleaseNoteItemsByReleaseNoteParent(ReleaseNoteModel releaseNoteItemParent)
{
return GetAllLiveReleaseNoteItemsByReleaseNoteParentId(releaseNoteItemParent.Id).Select(rn => new ReleaseNoteItemModel
{
Id = rn.Id,
Added = rn.GetValue("Added") is bool ? (bool)rn.GetValue("Added") : false,
BugId = rn.GetValue<string>("bug_id"),
BugStatus = rn.GetValue<Lstring>("bugStatus"),
Category = rn.GetValue<Lstring>("category"),
Component = rn.GetValue<Lstring>("component"),
#Content = rn.GetValue<Lstring>("content"),
Criticality = rn.GetValue<Lstring>("criticality"),
Customer = rn.GetValue<string>("customer"),
Date = rn.GetValue<DateTime?>("date"),
Grouped = rn.GetValue<string>("grouped"),
Override = rn.GetValue<string>("override"),
Patch_Num = rn.GetValue<string>("patch_num"),
PublishedDate = rn.PublicationDate,
Risk = rn.GetValue<Lstring>("risk"),
Title = rn.GetValue<string>("Title"),
Summary = rn.GetValue<Lstring>("summary"),
Prod_Name = rn.GetValue<Lstring>("prod_name"),
ReleaseNoteParent = releaseNoteItemParent,
McProductId = GetMcProductId(rn.GetRelatedItems("McProducts").Cast<DynamicContent>()),
}).ToList();
}
Is there any way to optimize this all into one query or a better way of doing this? Taking almost a minute to map this objects is too long for what we need to do with them.
If there is no way we will have to cache the items or make a SQL query. I would rather not do caching or SQL query if I do not have to.
Thank you in advance for any and all help you can provide, I am new to posting questions on stackoverflow so if you need any additional data please let me know.
Is there a reason why you are doing a .ToList() for the items? Is it possible for you to avoid that. In my opinion, most of the time(of the 1 minute) is taken to convert all your items into a list. Conversion from Sitefinity object to C# object is not the culprit here.
Look Arno's answer here: https://plus.google.com/u/0/112295105425490148444/posts/QrsVtxj1sCB?cfem=1
You can use the "Content links manager" to query dynamic modules relationships (both by parent -ParentItemId- or by child -ChildItemId-) much faster:
var providerName = String.Empty;
var parentTitle = "Parent";
var relatedTitle = "RelatedItem3";
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(providerName);
Type parentType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ParentModules.ParentModule");
Type relatedType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.RelatedModules.RelatedModule");
ContentLinksManager contentLinksManager = ContentLinksManager.GetManager();
// get the live version of all parent items
var parentItems = dynamicModuleManager.GetDataItems(parentType).Where(i => i.GetValue<string>("Title").Contains(parentTitle) && i.Status == ContentLifecycleStatus.Live && i.Visible);
// get the ids of the related items.
// We use the OriginalContentId property since we work with the live vesrions of the dynamic modules
var parentItemIds = parentItems.Select(i => i.OriginalContentId).ToList();
// get the live versions of all the schedules items
var relatedItems = dynamicModuleManager.GetDataItems(relatedType).Where(i => i.Status == ContentLifecycleStatus.Live && i.Visible && i.GetValue<string>("Title").Contains(relatedTitle));
// get the content links
var contentLinks = contentLinksManager.GetContentLinks().Where(cl => cl.ParentItemType == parentType.FullName && cl.ComponentPropertyName == "RelatedField" && parentItemIds.Contains(cl.ParentItemId) && cl.AvailableForLive);
// get the IDs of the desired parent items
var filteredParentItemIds = contentLinks.Join<ContentLink, DynamicContent, Guid, Guid>(relatedItems, (cl) => cl.ChildItemId, (i) => i.OriginalContentId, (cl, i) => cl.ParentItemId).Distinct();
// get the desired parent items by the filtered IDs
var filteredParentItems = parentItems.Where(i => filteredParentItemIds.Contains(i.OriginalContentId)).ToList();
I would imagine that every release note item under a single release note would be related to the same product wouldn't it?
If so, do you need to do the GetMcProductId method for every item?
I need to convert one object to another...in both cases of a single instance of the object as well as corresponding Lists of those objects. I'd rather not have to perform the same mappings in 2 places. Here is what I mean:
This converts a Facebook movie to a Standard Movie object
//Converts an Facebook object to a Standard Movie object
public MovieDetails ConvertFacebookMovieToStandardMovie(FacebookMovie Movies)
{
MovieDetails objMovieDetails = new MovieDetails();
objMovieDetails.ID = 0;
objMovieDetails.Source = Movies.Source;
objMovieDetails.SourceID = Convert.ToString(Movies.ID);
objMovieDetails.Title = Movies.Name;
objMovieDetails.URL = GetInternalMovieURL(objMovieDetails.Source, objMovieDetails.SourceID);
objMovieDetails.ImageURL = Movies.Picture;
objMovieDetails.SourceURL = Movies.SourceURL;
objMovieDetails.Description = Movies.Description
return objMovieDetails;
}//Convert Facebook to standard
Now I also need to do the same things in cases where I have the same objects, just in List form, i.e.
//Converts an Facebook class to a MovieDetails class of WWN
public List<MovieDetails> ConvertFacebookMovieToStandardMovie(List<FacebookMovie> lstFacebookMovieDetails)
{
List<MovieDetails> lstMovieListDetails = lstFacebookMovieDetails.Select(Movies => new MovieDetails()
{
ID = 0
,Source = Movies.Source
,SourceID = Convert.ToString(Movies.ID)
,Title = Movies.Name
,URL = GetInternalMovieURL(Source, SourceID)
,ImageURL = Movies.Picture
,SourceURL = Movies.SourceURL
,Description = Movies.Description
}).ToList();
return lstMovieListDetails;
}//Convert Facebook to standard
I'm new to c# and linq, so not sure how I could create one method that could handle both...or at least a way to encapsulate the mappings.
I know I could create an overload method for the List scenario and loop through the items in the list and call the first object convert method...But I was hoping to use the linq route I currently have for, what I'm guessing, is better performance.
Thanks!
Chad
Just call your function.
public List<MovieDetails> ConvertFacebookMovieToStandardMovie(List<FacebookMovie> lstFacebookMovieDetails)
{
List<MovieDetails> lstMovieListDetails =
lstFacebookMovieDetails.Select(Movies => ConvertFacebookMovieToStandardMovie(Movies)).ToList();
return lstMovieListDetails;
}
or the other way
public MovieDetails ConvertFacebookMovieToStandardMovie(FacebookMovie Movies)
{
return ConvertFacebookMovieToStandardMovie(new [] { Movies} ).FirstOrDefault();
}//Convert Facebook to standard
You could also overload as then
public static MovieDetails operator as(FacebookMovie m)
{
if (m == null) return null;
MovieDetails objMovieDetails = new MovieDetails()
{
ID = 0,
Source = Movies.Source,
SourceID = Convert.ToString(Movies.ID),
Title = Movies.Name,
URL = GetInternalMovieURL(objMovieDetails.Source, objMovieDetails.SourceID),
ImageURL = Movies.Picture,
SourceURL = Movies.SourceURL,
Description = Movies.Description
}
return objMovieDetails
}
then ConvertFacebookMovieToStandardMovie seems silly but it would look like this:
public MovieDetails ConvertFacebookMovieToStandardMovie(FacebookMovie Movies)
{
return Movies as MovieDetails;
}//Convert Facebook to standard
and
public List<MovieDetails> ConvertFacebookMovieToStandardMovie(List<FacebookMovie> lstFacebookMovieDetails)
{
List<MovieDetails> lstMovieListDetails =
lstFacebookMovieDetails.Select(Movies => Movies as MovieDetails).ToList();
return lstMovieListDetails;
}
Unless there's a performance gain on having separate code for these two cases I'd simply scrap the single movie variant and keep the List variant. To do the single movie variant simply do this with using System.Linq;:
List<FacebookMovie> moviesDetailsList = ...
MovieDetails movieDetails = ConvertFacebookMovieToStandardMovie(moviesDetailsList).Single();
If you want the single case as a separate method, you can place the above as the body of that method with "..." = new [] {movieDetail};
A shorter version
public List<MovieDetails> ConvertFacebookMovieToStandardMovie(List<FacebookMovie> lstFacebookMovieDetails)
{
return lstFacebookMovieDetails.ConvertAll(ConvertFacebookMovieToStandardMovie);
}
As per msdn
Converts the elements in the current List to another type, and returns a list containing the converted elements.
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();