Mongo DB with C# Driver sort after project - c#

We are planning to move from MS SQL to Mongo DB, but we are hesitating because of the issue we are currently encountering.
Here's the code for the projection:
ProjectionDefinition<PrimaryCustomerIndividual, SearchResponse> projectionDefinition = Builders<PrimaryCustomerIndividual>.Projection.Expression(
a => new SearchResponse()
{
AdditionalContacts = "",
Adviser = a.Adviser,
ClientSince = a.ClientSince,
ClientNextActivityId = a.Activities.First().ActivityId,
CreateDate = a.CreateDateTime,
CustomerId = a.CustomerID,
Email = a.Email,
LastNote = a.Notes.First().Notes,
LeadOrigin = a.LeadOrigin,
LeadType = a.LeadType,
Mobile = a.Mobile,
Name = a.FirstName + " " + a.LastName,
NextReview = a.NextReview,
PhysicalAddress = a.PhysicalAddress,
UserNextActivityId = 0,
}
);
With the code above we are returning a different object from our collection object, Our collection object is PrimaryCustomerIndividual and we are returning SearchResponse we did this because there are fields that was needed to be populated by other collections. We have thousands of data and we are planning to have a pagination which will return 100 records per page but right now I'm having problem on how to accurately get the first 100 and then the second 100 if there's a sort.
and here is the code on how we are retrieving the data from the database:
var filter = Builders<PrimaryCustomerIndividual>.Filter.Where(a => a.IsActive);
var customers = customerCollection.Find(filter).Project(projectionDefinition).ToEnumerable()
I'm curios if there's a way to sort the data by the field of SearchResponse instead of fields of PrimaryCustomerIndiviual after the projection definition without converting it to list first?

Related

How set OrderBy on GroupedEnumerable

My result set is not sorting. How do I set up OrderBy for type System.Linq.GroupedEnumerable
I've converted an application from Core 1.1 to 2.2. Everything ported over fine except one piece of logic that:
1) takes a response from a service call maps it to a GroupedEnumerable
2) takes the grouped set and passes it to a function that maps it to an object of type System.Linq.Enumerable.SelectEnumerableIterator.
The resulting object is properly populated but not sorted. I have tried the order by in the function parameter call and as a separate process afterwards.
//response = {myService.myClient.SearchNominationsResponse}
//groupedSet = {System.Linq.GroupedEnumerable<ServiceClients.myClient.NominationObject, long>}
//result = {System.Linq.Enumerable.SelectEnumerableIterator<System.Linq.IGrouping<long, ServiceClients.myClient.NominationObject>, app.ViewModels.EvaluationSummary>}
public IEnumerable<EvaluationSummary> GetEvaluationSummaries(string sortBy, string sortOrder, Filter filter = null)
{
var request = Mapper.MapSearchNominationRequest(filter);
request.IsDetailed = false;
var response = myService.SearchNominationsAsync(request).GetAwaiter().GetResult();
var groupedSet = response.mySet.GroupBy(n => n.SetId);
// I get a proper result but it is not sorted
var result = groupedSet.Select(
g => Mapper.MapEvaluationSummary(
g.OrderBy(g2 => sortBy + " " + sortOrder)
.Last()));
// Still not sorting
result = result.OrderBy(r => sortBy + sortOrder);
return result;
}
public EvaluationSummary MapEvaluationSummary(SetObject setIn)
{
var eval = new EvaluationSummary
{
setDate = setIn.Date,
setId = setIn.Id,
setTypeId = setIn.TypeId,
setTypeDescription = setIn.TypeDescription,
setStatusId = setIn.StatusId,
setStatusDescription = setIn.StatusDescription,
setBy = setIn.Manager,
setEmployee = setIn.employee
};
}
So in my view I have columns that list Date, Id, setEmployee. I can click on these values to issue a sort pattern and I can see that the sortBy and sortOrder variables are being passed in with proper values but the sorting is not happening.
I expect 'William' to appear before 'Bill' and then Bill to appear before 'William' when toggling the employee column header in my view.
Based off of the previous answers, I'm still not sure if I can substitute a property name in the LINQ with a variable. To fix my problem and move on I've implemented JS logic to sort my table headers. We had a custom JS that we use to format tables in our apps but it seems the sort functionality never worked. Anyway not an answer to my question but this is how I solved the problem:
Logic can be found at:
http://www.kryogenix.org/code/browser/sorttable/
-HaYen

asp.net / EntityFramework - Why does my application pool crashes?

I'm trying to add a new method to my API.
The method's goal is to return a list of Partenaires having the given Prestation in their prestations.
When I'm calling the method by a GET request, the application pool of my API crashes. In the event logs, I have a warning called Microsoft-Windows-WAS and the linked error is:
A process serving application pool 'UphairApi2' suffered a fatal communication error with the Windows Process Activation Service. The process id was '3960'. The data field contains the error number.
And the network developer tool says that it failed to load response data.
Failed to load response data
When I'm returning return db.Partenaires.Where(p => p.PartenairePrestations.Any(pp => pp.Prestation.NomPrestation == prestation.Value)).ToString();, here's the returned request:
"SELECT\r\nExtent1.IdPartenaire, \r\nExtent1.FirstName, \r\nExtent1.LastName, \r\nExtent1.Email, \r\nExtent1.Password, \r\nExtent1.PasswordSalt, \r\nExtent1.Type, \r\nExtent1.Pays, \r\nExtent1.Ville, \r\nExtent1.CodePostale, \r\nExtent1.Adresse, \r\nExtent1.Lat, \r\nExtent1.Lng, \r\nExtent1.ImageUrl, \r\nExtent1.CouvertureUrl, \r\nExtent1.DateNaissance, \r\nExtent1.ADomicile, \r\nExtent1.SeDeplace, \r\nExtent1.DateAjout, \r\nExtent1.AdresseComplement, \r\nExtent1.TelMobile, \r\nExtent1.ValidationAutomatique, \r\nExtent1.NotifEmailMessage, \r\nExtent1.NotifEmailReservation, \r\nExtent1.NotifEmailPaiement, \r\nExtent1.NotifEmailNewsletter, \r\nExtent1.NotifSmsMessage, \r\nExtent1.NotifSmsReservation, \r\nExtent1.IdUserMango, \r\nExtent1.Iban, \r\nExtent1.TitulaireCompte, \r\nExtent1.IdWallet, \r\nExtent1.IdAccount, \r\nExtent1.Valide, \r\nExtent1.Son, \r\nExtent1.Push, \r\nExtent1.IdPhone\r\nFROM Partenaire AS Extent1\r\n WHERE EXISTS(SELECT\r\n1 AS C1\r\nFROM PartenairePrestation AS Extent2 INNER JOIN Prestation AS Extent3 ON Extent2.IdPrestation = Extent3.IdPrestation\r\n WHERE (Extent1.IdPartenaire = Extent2.IdPartenaire) AND ((Extent3.NomPrestation = #p__linq__0) OR ((Extent3.NomPrestation IS NULL) AND (#p__linq__0 IS NULL))))"
And the equivalent for Mysql Workbench:
SELECT Extent1.IdPartenaire, Extent1.FirstName, Extent1.LastName, Extent1.Email, Extent1.Password, Extent1.PasswordSalt, Extent1.Type, Extent1.Pays, Extent1.Ville, Extent1.CodePostale, Extent1.Adresse, Extent1.Lat, Extent1.Lng, Extent1.ImageUrl, Extent1.CouvertureUrl, Extent1.DateNaissance, Extent1.ADomicile, Extent1.SeDeplace, Extent1.DateAjout, Extent1.AdresseComplement, Extent1.TelMobile, Extent1.ValidationAutomatique, Extent1.NotifEmailMessage, Extent1.NotifEmailReservation, Extent1.NotifEmailPaiement, Extent1.NotifEmailNewsletter, Extent1.NotifSmsMessage, Extent1.NotifSmsReservation, Extent1.IdUserMango, Extent1.Iban, Extent1.TitulaireCompte, Extent1.IdWallet, Extent1.IdAccount, Extent1.Valide, Extent1.Son, Extent1.Push, Extent1.IdPhone FROM Partenaire AS Extent1 WHERE EXISTS(SELECT 1 AS C1 FROM PartenairePrestation AS Extent2 INNER JOIN Prestation AS Extent3 ON Extent2.IdPrestation = Extent3.IdPrestation WHERE (Extent1.IdPartenaire = Extent2.IdPartenaire) AND ((Extent3.NomPrestation = 'Barbe')))
I tested this request in MysqlWorkbench and a set of datas is well returned.
Here's my method:
// GET: api/Partenaires_prestations
[Authorize]
[Route("api/Partenaires_prestations")]
public List<PartenaireMapItem> GetPartenairesWithPrestations() {
Random rnd = new Random();
var queryString = Request.GetQueryNameValuePairs();
var prestation = queryString.FirstOrDefault();
return db.Partenaires.Where(p => p.PartenairePrestations.Any(pp => pp.Prestation.NomPrestation == prestation.Value))
.ToList()
.Select(p => new PartenaireMapItem {
IdPartenaire = p.IdPartenaire,
FirstName = p.FirstName,
LastName = p.LastName,
NomComplet = p.LastName.Substring(0,1).ToUpper() + ". " + p.FirstName,
Type = p.Type,
DureeMin = 50,
Lat = p.Lat,
Lng = p.Lng,
ImageUrl = p.ImageUrl,
SeDeplace = p.SeDeplace,
ADomicile = p.ADomicile,
Notes = p.NoteClientPartenaires,
Prestations = p.PartenairePrestations.Select(y => y.Prestation.NomPrestation).ToList();
}).ToList();
}
What am I doing wrong ?
Any help would be appreciated since I couldn't find another related thread on internet.
You will probably need to debug the API and specify more detail to help narrow down a cause. A couple of things I can see:
var prestation = queryString.FirstOrDefault();
// Handle when prestation comes back #null. Is that valid?
var results = db.Partenaires.Where(p => p.PartenairePrestations.Any(pp => pp.Prestation.NomPrestation == prestation.Value))
// .ToList() // Avoid .ToList() here... Select the entity properties you need.
.Select(p => new PartenaireMapItem {
IdPartenaire = p.IdPartenaire,
FirstName = p.FirstName,
LastName = p.LastName,
// NomComplet = p.LastName.Substring(0,1).ToUpper() + ". " + p.FirstName, // Remove. Make this a computed property in your view model.
Type = p.Type,
// DureeMin = 50, // Can remove, can be a computed property.
Lat = p.Lat,
Lng = p.Lng,
ImageUrl = p.ImageUrl,
SeDeplace = p.SeDeplace, // Ok if a String/value.
ADomicile = p.ADomicile, // Ok if a String/value.
Notes = p.NoteClientPartenaires, // Ok if a String/value.
Prestations = p.PartenairePrestations.Select(y => y.Prestation.NomPrestation).ToList(); // Assuming this is retrieving the names of presentations. List<string>.
}).ToList();
return results;
The early .ToList() was required because you were attempting to compute values (NameComplet) in the Linq expression that normally would have been fed to EF which your DB provider will not understand. For efficiency, only select mapped properties, and instead change any computed values to read-only properties on your view model. (PartenaireMapItem)
private string _nomComplet = null;
public string NomComplet
{
get { return _nomComplet ?? (_nomComplet = LastName.Substring(0,1).ToUpper() + ". " + FirstName); }
}
That example buffers the result assuming that the name details are read-only. If First/Last name can be updated, just return the calculated name each time.
The other properties should be fine, assuming that SeDeclace/ADomicile are string values and not child entities. The same goes for the list of Prestations. A list of strings for the Prestation names should be fine.
The other minor change I made was to retrieve the view models in a variable to inspect prior to returning. This better facilitates using a breakpoint to inspect the results before returning. From here determine if any error is coming back from the computation of the results, or something else such as serializing the resulting view models back to the client.

Sitefinity: Dynamic Content query optimization with field values

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?

Can Entity Framework add many related entities with single SaveChanges()?

I am writing many (20+) parent child datasets to the database, and EF is requiring me to savechanges between each set, without which it complains about not being able to figure out the primary key. Can the data be flushed to the SQL Server so that EF can get the primary keys back from the identities, with the SaveChanges being sent at the end of writing all of the changes?
foreach (var itemCount in itemCounts)
{
var addItemTracking = new ItemTracking
{
availabilityStatusID = availabilityStatusId,
itemBatchId = itemCount.ItemBatchId,
locationID = locationId,
serialNumber = serialNumber,
trackingQuantityOnHand = itemCount.CycleQuantity
};
_context.ItemTrackings.Add(addItemTracking);
_context.SaveChanges();
var addInventoryTransaction = new InventoryTransaction
{
activityHistoryID = newInventoryTransaction.activityHistoryID,
itemTrackingID = addItemTracking.ItemTrackingID,
personID = newInventoryTransaction.personID,
usageTransactionTypeId = newInventoryTransaction.usageTransactionTypeId,
transactionDate = newInventoryTransaction.transactionDate,
usageQuantity = usageMultiplier * itemCount.CycleQuantity
};
_context.InventoryTransactions.Add(addInventoryTransaction);
_context.SaveChanges();
}
I would like to do my SaveChanges just once at the end of the big loop.
You don`t need to save changes every time if you use objects refernces to newly created objects not IDs:
var addItemTracking = new ItemTracking
{
...
}
_context.ItemTrackings.Add(addItemTracking);
var addInventoryTransaction = new InventoryTransaction
{
itemTracking = addItemTracking,
...
};
_context.InventoryTransactions.Add(addInventoryTransaction);
...
_context.SaveChanges();
Since they're all new items rather than
itemTrackingID = addItemTracking.ItemTrackingID,
you could go with
addItemTracking.InventoryTransaction = addInventoryTransaction;
(or whatever the associated navigation property is) and pull the _context.SaveChanges() out of the loop entirely. Entity Framework is very good at inserting object graphs when everything is new. When saving object graphs containing both new and existing items setting the associated id is always safer.
How about:
var trackingItems = itemCounts
.Select(i => new ItemTracking
{
availabilityStatusID = availabilityStatusId,
itemBatchId = i.ItemBatchId,
locationID = locationId,
serialNumber = serialNumber,
trackingQuantityOnHand = i.CycleQuantity
});
_context.ItemTrackings.AddRange(trackingItems);
_context.SaveChanges();
var inventoryTransactions = trackingItems
.Select(t => new InventoryTransaction
{
activityHistoryID = newInventoryTransaction.activityHistoryID,
itemTrackingID = t.ItemTrackingID,
personID = newInventoryTransaction.personID,
usageTransactionTypeId = newInventoryTransaction.usageTransactionTypeId,
transactionDate = newInventoryTransaction.transactionDate,
usageQuantity = usageMultiplier * t.trackingQuantityOnHand
});
_context.InventoryTransactions.AddRange(inventoryTransactions);
_context.SaveChanges();
However I haven't worked with EF for quite a while and above code is written in notepad so I cannot vouch for it

How do I order a sql datasource of uniqueidentifiers in Linq by an array of uniqueindentifiers

I have a string list(A) of individualProfileId's (GUID) that can be in any order(used for displaying personal profiles in a specific order based on user input) which is stored as a string due to it being part of the cms functionality.
I also have an asp c# Repeater that uses a LinqDataSource to query against the individual table. This repeater needs to use the ordered list(A) to display the results in the order specified.
Which is what i am having problems with. Does anyone have any ideas?
list(A)
'CD44D9F9-DE88-4BBD-B7A2-41F7A9904DAC',
'7FF2D867-DE88-4549-B5C1-D3C321F8DB9B',
'3FC3DE3F-7ADE-44F1-B17D-23E037130907'
Datasource example
IndividualProfileId Name JobTitle EmailAddress IsEmployee
3FC3DE3F-7ADE-44F1-B17D-23E037130907 Joe Blo Director dsd#ad.com 1
CD44D9F9-DE88-4BBD-B7A2-41F7A9904DAC Maxy Dosh The Boss 1
98AB3AFD-4D4E-4BAF-91CE-A778EB29D959 some one a job 322#wewd.ocm 1
7FF2D867-DE88-4549-B5C1-D3C321F8DB9B Max Walsh CEO 1
There is a very simple (single-line) way of doing this, given that you get the employee results from the database first (so resultSetFromDatabase is just example data, you should have some LINQ query here that gets your results).
var a = new[] { "GUID1", "GUID2", "GUID3"};
var resultSetFromDatabase = new[]
{
new { IndividualProfileId = "GUID3", Name = "Joe Blo" },
new { IndividualProfileId = "GUID1", Name = "Maxy Dosh" },
new { IndividualProfileId = "GUID4", Name = "some one" },
new { IndividualProfileId = "GUID2", Name = "Max Walsh" }
};
var sortedResults = a.Join(res, s => s, e => e.IndividualProfileId, (s, e) => e);
It's impossible to have the datasource get the results directly in the right order, unless you're willing to write some dedicated SQL stored procedure. The problem is that you'd have to tell the database the contents of a. Using LINQ this can only be done via Contains. And that doesn't guarantee any order in the result set.
Turn the list(A), which you stated is a string, into an actual list. For example, you could use listAsString.Split(",") and then remove the 's from each element. I’ll assume the finished list is called list.
Query the database to retrieve the rows that you need, for example:
var data = db.Table.Where(row => list.Contains(row.IndividualProfileId));
From the data returned, create a dictionary keyed by the IndividualProfileId, for example:
var dic = data.ToDictionary(e => e.IndividualProfileId);
Iterate through the list and retrieve the dictionary entry for each item:
var results = list.Select(item => dic[item]).ToList();
Now results will have the records in the same order that the IDs were in list.

Categories