LINQ - NOT selecting certain fields? - linq

I have a LINQ query mapped with the Entity Framework that looks something like this:
image = this.Context.ImageSet
.Where(n => n.ImageId == imageId)
.Where(n => n.Albums.IsPublic == true)
.Single();
This returns a single image object and works as intended.
However, this query returns all the properties of my Image table in the DB.
Under normal circumstances, this would be fine but these images contain a lot of binary data that takes a very long time to return.
Basically, in it current state my linq query is doing:
Select ImageId, Name, Data
From Images
...
But I need a query that does this instread:
Select ImageId, Name
From Images
...
Notice i want to load everything except the Data. (I can get this data on a second async pass)

Unfortunately, if using LINQ to SQL, there is no optimal solution.
You have 3 options:
You return the Entity, with Context tracking and all, in this case Image, with all fields
You choose your fields and return an anonymous type
You choose your fields and return a strongly typed custom class, but you lose tracking, if thats what you want.
I love LINQ to SQL, but thats the way it is.
My only solution for you would be to restructure your DataBase, and move all the large Data into a separate table, and link to it from the Image table.
This way when returning Image you'd only return a key in the new DataID field, and then you could access that heavier Data when and if you needed it.
cheers

This will create a new image with only those fields set. When you go back to get the Data for the images you select, I'd suggest going ahead and getting the full dataset instead of trying to merge it with the existing id/name data. The id/name fields are presumably small relative to the data and the code will be much simpler than trying to do the merge. Also, it may not be necessary to actually construct an Image object, using an anonymous type might suit your purposes just as well.
image = this.Context.ImageSet
.Where(n => n.ImageId == imageId)
.Where(n => n.Albums.IsPublic == true)
.Select( n => new Image { ImageId = n.ImageId, Name = n.Name }
.Single();

[If using Linq 2 SQL] Within the DBML designer, there is an option to make individual table columns delay-loaded. Set this to true for your large binary field. Then, that data is not loaded until it is actually used.
[Question for you all: Does anyone know if the entity frameworks support delayed loaded varbinary/varchar's in MSVS 2010? ]
Solution #2 (for entity framework or linq 2 sql):
Create a view of the table that includes only the primary key and the varchar(max)/varbinary(max). Map that into EF.
Within your Entity Framework designer, delete the varbinary(max)/varchar(max) property from the table definition (leaving it defined only in the view). This should exclude the field from read/write operations to that table, though you might verify that with the logger.
Generally you'll access the data through the table that excludes the data blob. When you need the blob, you load a row from the view. I'm not sure if you'll be able to write to the view, I'm not sure how you would do writes. You may be able to write to the view, or you may need to write a stored procedure, or you can bust out a DBML file for the one table.

You cannot do it with LINQ at least for now...
The best approach I know is create View for the table you need without large fields and use LINQ with that View.

Alternatively you could use the select new in the query expression...
var image =
(
from i in db.ImageSet
where i.ImageId == imageId && i.Albums.IsPublic
select new
{
ImageId = i.ImageId,
Name = i.Name
}
).Single()
The LINQ query expressions actually get converted to the Lambda expression at compile time, but I prefair using the query expression generally because i find it more readable and understandable.
Thanks :)

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.

Getting strings from LINQ queries in Sharepoint web part?

I'm setting up a Sharepoint Web Part for tracking language translations for our various products. I can create the translations, now I'm trying to re-populate the form controls if someone chooses an item that that already has a translation, ie they select "French-ProductX" and the various texboxes populate with the data for that translation/product.
My LINQ query is pulling the right data (itemname is the product the user has chosen in a dropdown menu).
var query = from translation in Translations
where (Convert.ToString(translation.Name)) == itemname
select translation;
I can bind the query to a grid and see i'm getting the correct data, but I'm stuck after that. There are two fields (to start with) i want to pull, "Description" and "Features" as strings and fill their corresponding asp:TextBoxes.
I tried converting to array, and it still binds correctly, but it isn't indexing the fields, its viewed aa a single item of type Translation.
I tried an IEnumerable query and converting to a dataview but I got casting errors on the WHERE clause I couldn't resolve.
I tried this, but VS says 'myitem" is an unassigned local variable.
var query = from translation in Translations
where (Convert.ToString(translation.Name)) == itemname
select new { myitem = translation.Description, features = translation.Features };
ItemDescription.Text = myitem;
This seems like it should be a simple thing, am I just missing something here?
What's happening is you are getting an IEnumerable of an anonymous type with the properties of myitem and features.
You would need to change the last line to:
ItemDescription.Text = query.First().myitem;

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).

Shaping EF LINQ Query Results Using Multi-Table Includes

I have a simple LINQ EF query below using the method syntax. I'm using my Include statement to join four tables: Event and Doc are the two main tables, EventDoc is a many-to-many link table, and DocUsage is a lookup table.
My challenge is that I'd like to shape my results by only selecting specific columns from each of the four tables. But, the compiler is giving a compiler is giving me the following error:
'System.Data.Objects.DataClasses.EntityCollection does not contain a definition for "Doc' and no extension method 'Doc' accepting a first argument of type 'System.Data.Objects.DataClasses.EntityCollection' could be found.
I'm sure this is something easy but I'm not figuring it out. I haven't been able to find an example of someone using the multi-table include but also shaping the projection.
Thx,Mark
var qry= context.Event
.Include("EventDoc.Doc.DocUsage")
.Select(n => new
{
n.EventDate,
n.EventDoc.Doc.Filename, //<=COMPILER ERROR HERE
n.EventDoc.Doc.DocUsage.Usage
})
.ToList();
EventDoc ed;
Doc d = ed.Doc; //<=NO COMPILER ERROR SO I KNOW MY MODEL'S CORRECT
DocUsage du = d.DocUsage;
Very difficult to know what is going on without a screencap of your model, including the navigational properties on each entity.
But if your saying it's a many-to-many between Event and Doc (with EventDoc being the join table), and assuming your join table has nothing but the FK's and therefore doesn't need to be mapped, then shouldn't a single Event have many Doc's?
This query:
var query = ctx.Event.Include("EventDoc.Doc");
Would imply (based on the lack of pluralization): a single Event has a single EventDoc which has a single Doc.
But shouldn't that be: a single Event has a single EventDoc which has many Doc's.
Therefore your projection doesn't really make sense. Your trying to project to an anonymous type, with EventDate and Filename for a single Doc, but an Event has many Docs.
Maybe a projection like this would be more suitable:
var query = ctx.Event.Include("EventDoc.Docs.DocUsage")
.Select(x => new
{
EventDate = x.EventDate,
DocsForEvent = x.EventDocs.Docs
}).ToList();
And for that you work you need to fix up your model. Im surprised it even validates/compiles.
Either your model is wrong or your description of the database cardinalities in your question is. :)
Of course, i could be completely misunderstanding your database and/or model - so if i am let me know and i'll remove this answer.

Linq stored procedure with dynamic results

So I'm extremely new to Linq in .Net 3.5 and have a question. I use to use a custom class that would handle the following results from a store procedure:
Set 1: ID Name Age
Set 2: ID Address City
Set 3: ID Product Price
With my custom class, I would have received back from the database a single DataSet with 3 DataTables inside of it with columns based on what was returned from the DB.
My question is how to I achive this with LINQ? I'm going to need to hit the database 1 time and return multiple sets with different types of data in it.
Also, how would I use LINQ to return a dynamic amount of sets depending on the parameters (could get 1 set back, could get N amount back)?
I've looked at this article, but didn't find anything explaining multiple sets (just a single set that could be dynamic or a single scalar value and a single set).
Any articles/comments will help.
Thanks
I believe this is what you're looking for
Linq to SQL Stored Procedures with Multiple Results - IMultipleResults
I'm not very familiar with LINQ myself but here is MSDN's site on LINQ Samples that might be able to help you out.
EDIT: I apologize, I somehow missed the title where you mentioned you wanted help using LINQ with Stored Procedures, my below answer does not address that at all and unfortunately I haven't had the need to use sprocs with LINQ so I'm unsure if my below answer will help.
LINQ to SQL is able hydrate multiple sets of data into a object graph while hitting the database once. However, I don't think LINQ is going to achieve what you ultimately want -- which as far as I can tell is a completely dynamic set of data that is defined outside of the query itself. Perhaps I am misunderstanding the question, maybe it would help if you provide some sample code that your existing application is using?
Here is a quick example of how I could hydrate a anonymous type with a single database call, maybe it will help:
var query = from p in db.Products
select new
{
Product = p,
NumberOfOrders = p.Orders.Count(),
LastOrderDate = p.Orders.OrderByDescending().Take(1).Select(o => o.OrderDate),
Orders = p.Orders
};

Resources