During an update of our website with Umbraco, we upgraded from 4.7.2 to 4.11.8.
In a CSHTML-File I had this code:
foreach(var item in Model.AncestorOrSelf("Master")
.Items.First()
.PublicationFolder.First()
.Children.Where("Visible")
.OrderBy("publicationType, date desc"))
It worked fine and sorted the collection first by publicationType and then by the newest date.
In the new version (4.11.8) it doesn't work anymore. It gives me an exception: At least one object must implement IComparable.
And if I write .OrderBy("publicationType", "date desc"), it doesn't affect the collection.
So is this a bug or am I doing anything wrong?
Is there a workaround?
I found a solution so I need to cast the collection to a List<DynamicNode> so that it works.
foreach (var item in ((List<DynamicNode>)#Model.AncestorOrSelf("Master")
.Items.First()
.PublicationFolder.First()
.Children.Where("Visible").Items)
.OrderBy(t => t.GetPropertyValue("publicationType"))
.ThenByDescending(t => t.GetPropertyValue("date")))
The Where("Visible") extension returns IQueryable<DynamicNode> so you should be able to apply the LINQ OrderBy() and OrderByDescending() syntax for this query. You may need to alter certain bits though:
foreach(var item in Model.AncestorOrSelf("Master")
.Items.First()
.PublicationFolder.First()
.Children.Where("Visible")
.Cast<DynamicNode> // This line is optional
.OrderBy(x => x.GetPropertyValue("publicationType")
.ThenByDescending(x => x.GetPropertyValue("date"))
I have included an optional line to show that you can explicitly cast the dynamic objects to DynamicNode objects and you can then use intellisense in your view.
If memory serves you should be able to chain the OrderBys
foreach(var item in Model.AncestorOrSelf("Master").Items.First().PublicationFolder.First().Children.Where("Visible").OrderBy("publicationType").OrderByDescending("date"))
Related
I have the following LINQ to Entities call to fill a collection variable:
var insuredFamily = db.Insureds.Where(x => x.ssn.Split('-')[0] == tmp[MemberId])
.OrderBy(x => x.fk_relation);
How would I go about looping through the items in the collection printing out both the item header and the item value for each row?
I'm new to LINQ to Entities, so all I'm trying to do is loop through a built collection and output the headers and rows to a log file so that I can quickly see what is being returned in the collection without resorting to stepping through each row in the debugger. Is this possible?
If a generic loop is not possible, is it possible to set the data source of a data grid to the collection variable and view the collection in a grid?
For starters, this can't be a LINQ-to_Entities statement. There's no way to translate x.ssn.Split('-')[0] == tmp[MemberId] into SQL. Unless this is EF-Core, that automatically switches to client-side evaluation when the expression contain not-supported part. You're not explicit about that.
Anyway, it's overkill to add logging just to see the content of a collection while debugging. You can use the immediate window in Visual Studio to output data when the code stops at a break point.
To make this really helpful, override the Insured class's ToString method. When I do this with just some Product class ...
public override string ToString()
{
return string.Format("{0}, {1}", this.Name, this.UnitPrice);
}
... and break in the debugger right after a line like ...
var prod = context.Products;
.. then in the immediate window I can type ...
?prod.ToList()
... which gives me:
Count = 6
[0]: {Nike Air, 150.00}
[1]: {Web cam, 23.00}
[2]: {Mouse, 7.00}
[3]: {Cool pack, 4.50}
[4]: {Keyboard, 47.30}
[5]: {Action cam, 73.00}
(and some SQL logging)
Note that this is invasive debugging. By calling ToList, the query is forced to execute. This may change the state in a way that it affects the code's behavior. The same would hold for logging though.
Thank you for all the advice, but it looks like the following two lines of code will do what I need. I just wanted to visualize the results of my LINQ to SQL queries.
var nationalities = db.Nationalities.OrderBy(x => x.pk_Nationality);
dgvResults.DataSource = nationalities;
This code will simply put the collection into the data source of my data grid view allowing me to see all the contents.
Here is a very basic example of what I want to do. The code I have come up with seems quite verbose... ie looping through the collection, etc.
I am using a Telerik MVC grid that posts back a collection of deleted, inserted and updated ViewModels. The view models are similar but not exactly the same as the entity.
For example... I have:
Order.Lines. Lines is an entity collection (navigation property) containing OrderDetail records. In the update action of my controller using the I have a List names DeletedLines pulled from the POST data. I also have queried the database and have the Order entity including the Lines collection.
Now I basically want to tell it to delete all the OrderDetails in the Lines EntityCollection.
The way I have done it is something like:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.Where(l => l.Key == line.Key).SingleOrDefault())
}
I was hoping there was a way that I could use .Interset() to get a collection of entities to delete and pass that to DeleteObject.. however, DeleteObject seems to only accept a single entity rather than a collection.
Perhaps the above is good enough.. but it seemed like there should be an easier method.
Thanks,
BOb
Are the items in DeletedLines attached to the context? If so, what about this?
foreach (var line in DeletedLines) db.DeleteObject(line);
Response to comment #1
Ok, I see now. You can make your code a bit shorter, but not much:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.SingleOrDefault(l => l.Key == line.Key))
}
I'm not sure if DeleteObject will throw an exception when you pass it null. If it does, you may be even better off using Single, as long as you're sure the item is in there:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.Single(l => l.Key == line.Key))
}
If you don't want to re-query the database and either already have the mapping table PK values (or can include them in the client call), you could use one of Alex James's tips for deleting without first retrieving:
http://blogs.msdn.com/b/alexj/archive/2009/03/27/tip-9-deleting-an-object-without-retrieving-it.aspx
when using a MVC 3 collection that uses IEnumerable, is there a way to make small queries work to find single values? I've been experimenting with little success.
I have a collection of settings that have descriptions and settings. The problem is exposing one of them, as each is unique. I've tried something like this:
var appl = _service.GetSettings(id, app); //Call to service layer & repository
appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login")));
With little success (unless I'm missing something). Is it possible to select one item from an enumerable collection and either pass it to a ViewBag or ViewData object? What I would like to do is something like the following:
var appl = _service.GetSettings(id, app); //Call to service layer & repository
ViewBag.Login = appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login")));
And pass this to the view, since I have a view that now combines a collection with single values.
The following seems to work within the view:
Application Name #Html.TextBox("application_name", #Model.FirstOrDefault().app_name)
But I'm not sure if this violates separation of concerns. Am I on the wrong path here? Thank you!
EDIT: Here is what I needed. The answers below got me very very close +1 +1
ViewBag.Login = appl.Where(a => a.setting_name.StartsWith("Login")).FirstOrDefault().setting_value
ViewBag.Login = appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login"))).FirstOrDefault();
This will select the first object that matches your criteria and return a single result or null
var appl = _service.GetSettings(id, app);
ViewBag.Login = appl
.Where(a => a.setting_name.StartsWith("Login"))
.FirstOrDefault();
I have an application that manages documents called Notes. Like a blog, Notes can be searched for matches against one or more Tags, which are contained in a Note.Tags collection property. A Tag has Name and ID properties, and matches are made against the ID. A user can specify multiple tags to match against, in which case a Note must contain all Tags specified to match.
I have a very complex LINQ query to perform a Note search, with extension methods and looping. Quite frankly, it has a real code smell to it. I want to rewrite the query with something much simpler. I know that if I made the Tag a simple string, I could use something like this:
var matchingNotes = from n in myNotes
where n.Tags.All(tag => searchTags.Contains(tag))
Can I do something that simple if my model uses a Tag object with an ID? What would the query look like. Could it be written in fluent syntax? what would that look like?
I believe you can find notes that have the relevant tags in a single LINQ expression:
IQueryable<Note> query = ... // top part of query
query = query.Where(note => searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id)));
Unfortunately there is no “fluent syntax” equivalent for All and Any, so the best you can do there is
query = from note in query
where searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id))
select note;
which is not that much better either.
For starters see my comment; I suspect the query is wrong anyway! I would simplifiy it, by simply enforcing separately that each tag exists:
IQueryable<Note> query = ... // top part of query
foreach(var tagId in searchTagIds) {
var tmpId = tagId; // modified closures...
query = query.Where(note => note.Tags.Any(t => t.Id == tmpId));
}
This should have the net effect of enforcing all the tags specified are present and accounted for.
Timwi's solution works in most dialects of LINQ, but not in Linq to Entities. I did find a single-statement LINQ query that works, courtesy of ReSharper. Basically, I wrote a foreach block to do the search, and ReSharper offered to convert the block to a LINQ statement--I had no idea it could do this.
I let ReSharper perform the conversion, and here is what it gave me:
return searchTags.Aggregate<Tag, IQueryable<Note>>(DataStore.ObjectContext.Notes, (current, tag) => current.Where(n => n.Tags.Any(t => t.Id == tag.Id)).OrderBy(n => n.Title));
I read my Notes collection from a database, using Entity Framework 4. DataStore is the custom class I use to manage my EF4 connection; it holds the EF4 ObjectContext as a property.
I have an observable collection and I sort it using linq. Everything is great, but the problem I have is how do I sort the actual observable collection? Instead I just end up with some IEnumerable thing and I end up clearing the collection and adding the stuff back in. This can't be good for performance. Does anyone know of a better way to do this?
If you are using Silverlight 3.0, then using CollectionViewSource is the cleanest way. Refer below example: (it can be done via xaml as well)
ObservableCollection<DateTime> ecAll = new ObservableCollection<DateTime>();
CollectionViewSource sortedcvs = new CollectionViewSource();
sortedcvs.SortDescriptions.Add(new System.ComponentModel.SortDescription("Date",
System.ComponentModel.ListSortDirection.Ascending));
sortedcvs.Source = ecAll;
ListBoxContainer.DataContext = sortedcvs;
And in corresponding xaml set
ItemsSource="{Binding}"
for the ListBox or any ItemsControl derived control
Since the collection doesn't provide any Sort mechanism, this is probably the most practical option. You could implement a sort manually using Move etc, but it will probably be slower than doing in this way.
var arr = list.OrderBy(x => x.SomeProp).ToArray();
list.Clear();
foreach (var item in arr) {
list.Add(item);
}
Additionally, you might consider unbinding any UI elements while sorting (via either approach) you only pay to re-bind once:
Interestingly, if this was BindingList<T>, you could use RaiseListChangedEvents to minimise the number of notifications:
var arr = list.OrderBy(x => x).ToArray();
bool oldRaise = list.RaiseListChangedEvents;
list.RaiseListChangedEvents = false;
try {
list.Clear();
foreach (var item in arr) {
list.Add(item);
}
} finally {
list.RaiseListChangedEvents = oldRaise;
if (oldRaise) list.ResetBindings();
}
Note that in Linq, you are given an IEnumerable from your query, and that query has not executed yet. Therefore, the following code only runs the query once, to add it to an ObservableCollection:
var query = from x in Data
where x.Tag == "Something"
select x;
foreach(var item in query)
MyObservableCollection.Add(item);
Take a look at the "OrderBy" extension on IEnumerable:
foreach(var item in query.OrderBy(x => x.Name))
MyObservableCollection.Add(item);
ObservableCollections aren't designed to be sortable. List is sortable, and that's the underlying mechanism used by the answer referencing List.Sort(), but ObservableCollection isn't derived from List so you're out of luck there. Imo, the "right" solution is not to try to sort the ObservableCollection, but to implement ICollectionView and bind an instance of that to your control. That interface adds methods for sorting and has the additional benefit that its recognized by Silverlight controls (well, the ones that support it anyway such as DataGrid) so your sorting could be utilized directly from the UI layer. This question might be helpful:
Silverlight and icollectionview
i followed the link mentioned in this post http://mokosh.co.uk/post/2009/08/04/how-to-sort-observablecollection/comment-page-1/#comment-75
but having issues getting it to work in Silverlight
I created a property public SortableObservableCollection Terms
When I call Terms.Sort(new TermComparer()) the records are still display unsorted on the UI
could some suggest what could be going wrong. thanks
I found this on CodePlex:
Sorted Collections
Haven't used it yet though.
Rick