How to filter custom columns from SharePoint list using LINQ? - linq

My requirement is to get items, only from custom-columns, in a SharePoint list using LINQ.
Since my custom columns are created dynamically based on some calculations done on another SPList, it keeps increasing and decreasing in the count frequently, therefore I cannot use SPMetal.
I need to include a condition (!(SPBuiltInFieldId.Contains(field.Id))) to check if the items are taken only from custom fields in the following query.
List<SPListItem> AllResponses = (from SPListItem Response in oList.Items
select Response).ToList();
Please advice. Thanks!

I'm not sure it's possible to have a list item that contains only custom columns. Even if Title is not there, you'll have ID, Modified, Created, etc. Plus, there will be a number of hidden built-in columns.
If you want a list of items that contain items with custom fields, you can try something like the following, which utilizes Where and Any methods:
List<SPListItem> AllResponses =
(from SPListItem Response in oList.Items select Response)
.Where(item => item.Fields
.Cast<SPField>()
.Any(field => !SPBuiltInFieldId.Contains(field.Id)))
.ToList();
This will return all items that have at least one custom field.

Related

Linq to SharePoint broken after converting columns to content types

We're working on a project that uses Linq to SharePoint. The list had several columns. After using SPMetal to make the class, it was imported into VS to access the data context. The Linq queries worked fine.
We went in a different direction, by deleting the list columns and using content types with site columns. OOTB, the add/edit forms work fine. But after updating the class with SPMetal and importing the class into VS for the data context, all the Linq query show as errors. Visual Studio cannot recognize the columns any longer because they don't appear to be there in the data context from the updated class. The columns are in the content types now, instead of the list.
Is there a way to get the content type's columns to export in the class file with SPMetal? Is there another library to import to write Linq to SharePoint queries with lists that have content types? How do you write Linq queries that use content type columns?
So say you have a list called Documents. It has two columns called 'One' and 'Two'. You make your Linq to SP queries just fine:
DataContext dc = new DataContext("http://sharepoint");
var varResults = (from item in dc.Documents
where item.Two == "blah"
orderby item.One descending
select item);
Then you decide you want to use content types with site columns. The above query breaks when you delete columns 'One' and 'Two' from the list. You make site columns and assign them to a content type called 'Master', parent being item. Master has two content types deriving from it called 'CloneA' and 'CloneB'. Since the clone content type's parent is Master, then they automatically get it's site columns. When you assign the content types to the list, the definition looks like:
Column - Content types
Title - Documents, Master, CloneA, CloneB
One - Master, CloneA, CloneB
Two - Master, CloneA, CloneB
The clone content types will later be used for different Information Policies for retention on the Documents list. After breaking the inheritance and setting up the retention policies on the content types, now items can individually set to a content type which will cause the retention (1 day - CloneA, 1 week - CloneB) to kick off.
But the linq to SP queries are still broken. Even though the site columns show up, SPMetal only captures the bases content type for some reason. So to linq, the columns are not really there with the above query. Typing "where item." the 'Two' doesn't even show up. You have to cast it to make it work (probably not explaining it right). So here's the working code:
DataContext dc = new DataContext("http://sharepoint");
var varResults = (from item in dc.Documents.OfType<Master>()
where item.Two == "blah"
orderby item.One descending
select item);
You may be tempted to use
var varResults = (from item in dc.Documents.OfType<DocumentsMaster>()
Unfortunately, that will only return the items that are associated with that content type in the list. So if you want to get items of a certain content type to filter, knock yourself out.

dynamically select fields in linq

I have this here:
Dim query = FromTableRows.Select(Function(Row) Row.Item(_SqlSyntaxChecker.SelectedFields(0)))
Row is a normal DataRow, so I can get the field value of the rows like this: Row.Item(0), Row.Item(1), etc.
SelectedFields contains the field names of the FromTableRows.
Now, I would like to select all the fields in the SelectedFields list, while the number of the selected fields can vary.
Is this possible? How should I modify the code?
Thanks.
You can simply make use of the ItemArray property, if I understand your question correctly.
FromTableRows.Select(Function(Row) Row.ItemArray)
The ItemArray property is an object array that contains the elements found in the DataRow. You will, of course, lose any mapping via this method from columns to elements, but it sounds like that's what you want.

Concatenating a LINQ query and LINQ sort statement

I'm having a problem joining two LINQ queries.
Currently, my (original) code looks like this
s.AnimalTypes.Sort((x, y) => string.Compare(x.Type, y.Type));
What I'm needing to do is change this based on a date, then select all data past that date, so I have
s.AnimalTypes.Select(t=>t.DateChanged > dateIn).ToList()
s.AnimalTypes.Sort((…
This doesn't look right as it's not sorting the data selected, rather sorting everything in s.AnimalTypes.
Is there a way to concatenate the two LINQ lines? I've tried
s.AnimalTypes.Select(t=>t.DateChanged > dateIn).ToList().Sort((…
but this gives me an error on the Sort section.
Is there a simple way to do this? I've looked around and Grouo and OrderBy keep cropping up, but I'm not sure these are what I need here.
Thanks
From your description, I believe you want something like:
var results = s.AnimalTypes.Where(t => t.DateChanged > dateIn).OrderBy(t => t.Type);
You can call ToList() to convert to a List<T> at the end if required.
There are a couple of fundamental concepts I believe you are missing here -
First, unlike List<T>.Sort, the LINQ extension methods don't change the original collections, but rather return a new IEnumerable<T> with the filtered or sorted results. This means you always need to assign something to the return value (hence my var results = above).
Second, Select performs a mapping operation - transforming the data from one form to another. For example, you could use it to extract out the DateChanged (Select(t => t.DateChanged)), but this would give you an enumeration of dates, not the original animal types. In order to filter or restrict the list with a predicate (criteria), you'd use Where instead.
Finally, you can use OrderBy to reorder the resulting enumerable.
You are using Select when you actually want to use Where.
Select is a projection from one a collection of one type into another type - you won't increase or reduce the number of items in a collection using Select, but you can instead select each object's name or some other property.
Where is what you would use to filter a collection based on a boolean predicate.

How can I get the IQueryable object used by LinqDataSource?

Is there a way to get the IQueryable object that the LinqDataSource has used to retrieve data? I thought that it might be possible from the selected event, but it doesn't appear to be.
Each row in my table has a category field, and I want to determine how many rows there are per category in the results.
I should also note that I'm using a DataPager, so not all of the rows are being returned. That's why I want to get the IQueryable, so that I can do something like
int count = query.Where(i => i.Category == "Category1").Count();
Use the QueryCreated event. QueryCreatedEventArgs has a Query property that contains the IQueryable.
The event is raised after the original LINQ query is created, and contains the query expression before to it is sent to the database, without the ordering and paging parameters.
There's no "Selected" event in IQueryable. Furthermore, if you're filtering your data on the server, there'd be no way you can access it, even if the API exposed it, but to answer a part of the question, let's say you have category -> product where each category has many products and you want the count of the products in each category. It'd be a simple LINQ query:
var query = GetListOfCategories();
var categoryCount = query.Select(c => c.Products).Count();
Again, depending on the type of object GetListOfCategories return, you might end up having correct value for all the entries, or just the ones that are loaded and are in memory, but that's the different between Linq-to-Objects (in memory) and Linq-to-other data sources (lazy loaded).

Ultragrid : how to best add a set of sub rows programatically?

I have an Infragistics Ultragrid that is being used to display a list of attributes. Sometimes the attribute is an array so I am adding a sub row for each element so the user can optionally expand the row showing the array attribute and see all the element values.
So for each element I use:
var addedRow = mGrid.DisplayLayout.Bands[1].AddNew();
which if I have 300 elements gets called 300 times and takes around 9 seconds (I have profiled the application and this call is taking 98% of the elapsed time)
Is there a way to add these sub rows more efficiently?
I know I'm late with an answer, but hopefully someone can use my answer anyway. Whenever I need to set rows and subrows for ultragrid, I simply set the datasource by using linq and anonymous types to generate the propper collection.
say you have a list of persons (id, Name), and a list of cars (id, CarName, and OwnerId (personId))
now you like to show a gridview showing all persons, with an expandabel subrow providing which cars they own. simply do the following.
List<Person> persons = GetAllPersons();
List<Car> cars = GetAllCars();
grid.DataSource = persons.Select(x => new {x.Id, x.Name, Cars = cars.Where(z => z.OwnerId == x.Id).ToList()}).ToList();
Note the anonymous type I make, this will generate a list of objects having an id, Name, and a collection of cars. Also note that I call the ToList method twice in the last line, this is necessary in order to get ultragrid to bind properly.
Note further more that if you need to edit the gridview, the above method migth not be sufficient, as the ultragrid needs an underlaying datasource for modifying, and I dont believe that this will cope. BUT on the internet you'll find some extensions that can copy a Linq collection into a DataTable, doing that and then you should also be able of editing the grid.
I have often used the above method and it performs extremely well, even for huge collections.
Hope this helps somebody
you might want to use ultraGrid1.BeginUpdate(); and ultraGrid1.EndUpdate(true); to stop screen from repainting. made huge performance benefit for my app.
Also in my case I was populating nearly >10,000 rows, so have used UltraDataSource

Resources