Understanding ClientContext.Load's Parameters - c#

I have some code which makes calls to a SharePoint Managed Metadata Service that starts off like this :
var clientContext = new ClientContext("http://mysharepointsite/")
{ AuthenticationMode = ClientAuthenticationMode.Default};
var taxonomySession = TaxonomySession.GetTaxonomySession(clientContext);
var termStore = taxonomySession.GetDefaultSiteCollectionTermStore();
which I have no problem with. However, after this we have :
clientContext.Load(termStore,
store => store.Name,
store => store.Groups.Include(
group => group.Name,
group => group.TermSets.Include(
termSet => termSet.Name,
termSet => termSet.Terms.Include(
term => term.Name)
)
)
);
Can anyone please help me understand what is going on here?
At first I thought this was some kind of LINQ query, but then I would expect the class to have the line using System.Linq;, which it does not.
I just noticed that in Visual Studio there is some IntelliSense which says the call is structured like this : void ClientruntimeContext.Load<T>(T clientObject, params System.Linq.Expressions.Expression<Func<T, object>>[] retrievals) - which makes it seem like it is using Linq in some way
I understand that the code is in some way "loading" the termstore data in the managed metadata service from the given sharepoint site, but I don't quite understand what exactly that syntax is doing.
I got the code sample from here, and it does exactly what I want it to do but I would feel a lot more comfortable if I actually understood that syntax!
The documentation was also not particularly helpful, as it just defines Load()s parameters as <T>, which could be anything!
Any advice or recommended reading is much appreciated, thank you!

ClientRuntimeContext.Load<T> Method
The second parameter of this method specifies what properties of target client object (first parameter) should be retrieved using lambda expressions.
Examples
In the following query, all the properties except collection properties such as TermStore.Groups of TermStore client object will be retrieved
ctx.Load(termStore);
In the next query only the explicitly specified list of properties (TermStore.Name, TermStore.Groups) will be retrieved for TermStore client object:
ctx.Load(termStore, store => store.Name, store => store.Groups);
The next question arise, how to specify what properties of collection client object to retrieve, Include<TSource>(IQueryable<TSource>, \[\]) method method comes to the rescue here.
Include<TSource>(IQueryable<TSource>, \[\]) method
This method is used to limit what properties are returned from a collection of objects ( for performance purposes)
Example
The following expression
ctx.Load(termStore, store => store.Groups.Include( g => g.Name));
tells to construct the query to return TermStore client object which includes TermStore.Groups property but not the default properties of Group client object, only Group.Name property.

Related

Query Interceptors and complex lambda expressions

So we have a WCF Data Service exposing OData with an Entity Framework (v5.6) model.
Since we wanted to filter the data based on authentication and access control, some Query Interceptors were added. At first, all they had to do was get the user's scope and match that to an entity's property - something like this:
[QueryInterceptor("Entity")]
public Expression<Func<Entity, bool>> EntitiesInterceptor()
{
// Get user scope
string userScope = GetUserScope();
return entity => entity.Scope.StartsWith(userScope));
}
This worked as expected, and everything was well with the world.
Later on, we wanted to evolve the interceptor to allow matching with a list of scopes. So we changed the code to something like this:
// Get user scopes
string[] validScopes = GetUserScopes();
return entity => validUnits.Any(scope => entity.Scope.StartsWith(scope));
This seemed to work as long as our OData requests didn't expand beyond one level of relations. So, for instance, stuff like:
/ODataService.svc/Entity?$expand=OtherEntity
/ODataService.svc/Entity?$expand=OtherEntity,AnotherEntity
...would process just fine. However, when we start querying deeper relations:
/ODataService.svc/Entity?$expand=OtherEntity/YetAnotherEntity
We start getting an 'Object reference not set to an instance of an Object' error from System.Entity.Data. The stack trace is really long and mentions lots of classes on the System.Data namespace. Sadly, it does not mention any line in our code.
Link to full stack trace
It seems to us that the problem is on how EF is converting our lambda expressions to queries. We've tried replacing the .Any() with other equivalents like .Count(expression) > 0 in hopes that the conversion would work, but alas, the result was the same.
So, can anyone think of a different way to approach the problem? Has somebody faced the same issue? Or is it something that we're doing wrong?
Thanks in advance for reading the huge wall of text above.

MongoDB LinQ "Select" method will really retrieve only a subset of fields?

Searching across the internet how to retrieve a subset of fields in MongoDB, using C# official driver (but using LinQ as the base architecture) I found how to do this in MongoDB shell.
// selecting only "field" of a collection
db.collection.find( { field : 'value' }, { field: 1 } );
Then, I found at C# LinQ Tutorial the Select method, which is equivalent to this:
collection.AsQueryable<T>().Select(x => new { x.field });
However, the tutorial says the method "is used to project a new result type from the matching documents".
How to ensure this method will retrieve only the subset of fields and not the entire result and then select only the subset into a new object?
Will the driver build the query command before retrieve the results?
The driver does not currently retrieve a subset of the fields. If you need that functionality, you'll need to do it manually. The ticket for this functionality is here: https://jira.mongodb.org/browse/CSHARP-456. Feel free to leave feedback or vote it up if you need this.
This is cheating... but:
//This actual implementation is untested and may contain small errors.
//The helper method has been tested and *should* work.
public static IMongoQuery GetMongoQuery<T>(this IQueryable<T> query)
{
return ((MongoQueryable<T>)query).GetMongoQuery();
}
var temp =
from x in DB.Foo.AsQueryable<Test>()
where x.SomeField > 5;
select (x.OtherField);
return temp.GetMongoQuery().ToJson();

c# multiple expression parameteres

I'm trying to create a method signature that takes multiple properties of various type using
I would call it something like this:
AllPropertiesExcept(() => Property1, () => Property2)
This method almost work, except that the type of the properties have to be the same. I'm only going to use the property name, but want to use lambda expression to enable easy refactoring.
public static string MyMethod<T>(params Expression<Func<T>>[] propertyExpression)
I would use AllPropertiesExcept(params Expression<Func<object>>[] properties), you can still get the property names out of it, but it doesn't matter what type the property is.
Edit: However, I would tend to use it the other way round - instead of excluding properties I don't want to see, I would include properties I want to see. The reason is simple - to make your way work, you still need reflection - with my way, you could easily use the Func you get to get the actual data directly.
Edit 2 (getting the property name out of an expression):
Expression<Func<object>> obj = something; // you get this in your method
((obj.Body as UnaryExpression).Operand as MemberExpression).Member.Name
I can really advise you to use LinqPad for such things, you can easily drill down objects via Dump(), which displays the objects very user friendly. Just recreate a small example and experiment.
Does the method AllPropertiesExcept() return anything? Otherwise you could make a fluent interface (using method chaining):
AllPropertiesExcept(() => Property1)
.And(() => Property2)
.And(() => Property3);
Even if the AllPropertiesExcept() method returns something, you can defer the execution until you invoke a method at the end of the method chain:
var foo = AllPropertiesExcept(() => Property1)
.And(() => Property2)
.And(() => Property3)
.DoSomeThing();
I think what you need is to understand the ModelMetadata class documented here:
http://msdn.microsoft.com/en-us/library/system.web.mvc.modelmetadata.aspx
This class is used in ASP.NET MVC in situations like Html.LabelFor(x -> x.Name)
An expression is passed to the ModelMetadata.FromLambdaExpression Method documented here:
http://msdn.microsoft.com/en-us/library/ee428393.aspx
After understanding how it is used in MVC, you could create your own code with some informed knowledge of how it was applied elsewhere.

Getting magic strings out of QueryOver (or Fluent NHibernate perhaps)?

One of the many reason to use FluentNHibernate, the new QueryOver API, and the new Linq provider are all because they eliminate "magic string," or strings representing properties or other things that could be represented at compile time.
Sadly, I am using the spatial extensions for NHibernate which haven't been upgraded to support QueryOver or LINQ yet. As a result, I'm forced to use a combination of QueryOver Lambda expressions and strings to represent properties, etc. that I want to query.
What I'd like to do is this -- I want a way to ask Fluent NHibernate (or perhaps the NHibernate QueryOver API) what the magic string "should be." Here's a pseudo-code example:
Currently, I'd write --
var x = session.QueryOver<Shuttle>().Add(SpatialRestrictions.Intersects("abc", other_object));
What I'd like to write is --
var x = session.QueryOver<Shuttle>().Add(SpatialRestriction.Intersects(session.GetMagicString<Shuttle>(x => x.Abc), other_object));
Is there anything like this available? Would it be difficult to write?
EDIT: I just wanted to note that this would apply for a lot more than spatial -- really anything that hasn't been converted to QueryOver or LINQ yet could be benefit.
update
The nameof operator in C# 6 provides compile time support for this.
There is a much simpler solution - Expressions.
Take the following example:
public static class ExpressionsExtractor
{
public static string GetMemberName<TObj, TProp>(Expression<Func<TObj, TProp>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
return null;
return memberExpression.Member.Name;
}
}
And the usage:
var propName = ExpressionsExtractor.GetMemberName<Person, int>(p => p.Id);
The ExpressionsExtractor is just a suggestion, you can wrap this method in whatever class you want, maybe as an extension method or preferably a none-static class.
Your example may look a little like this:
var abcPropertyName = ExpressionsExtractor.GetMemberName<Shuttle, IGeometry>(x => x.Abc);
var x = session.QueryOver<Shuttle>().Add(SpatialRestriction.Intersects(abcPropertyName, other_object));
Assuming I'm understanding your question what you might want is a helper class for each entity you have with things like column names, property names and other useful things, especially if you want to use ICriteria searches. http://nhforge.org/wikis/general/open-source-project-ecosystem.aspx has plenty of projects that might help. NhGen (http://sourceforge.net/projects/nhgen/) creates very simple helper classes which might help point you down a design path for what you might want.
Clarification Edit: following an "I don't understand" comment
In short, I don't beleive there is a solution for you just yet. The QueryOver project hasn't made it as far as you want it to. So as a possible solution in the mean time, to remove magic strings build a helper class, so your query becomes
var x = session.QueryOver<Shuttle>().Add(SpatialRestrictions.Intersects(ShuttleHelper.Abc, other_object));
That way your magic string is behind some other property ( I just chose .Abc to demonstrate but I'm sure you'll have a better idea of what you want ) then if "abc" changes ( say to "xyz" ) you either change the property name from .Abc to .Xyz and then you will have build errors to show you where you need to update your code ( much like you would with lambda expressions ) or just change the value of the .Abc property to "xyz" - which would really only work if your property had some meaningfull name ( such as .OtherObjectIntersectingColumn etc ) not that property name itself. That does have the advantage of not having to update code to correct the build errors. At that point your query could be
var x = session.QueryOver<Shuttle>().Add(SpatialRestrictions.Intersects(ShuttleHelper.OtherObjectIntersectingColumn, other_object));
I mentioned the open source project ecosystem page as it can give you some pointers on what types of helper classes other people have made so your not re-inventing the wheel so to speak.

Linq2SQL inherited types and OfType query

I have a setup where I used Linq2SQL inheritance. To make queries easier, I expose the derived types in the DataContext as well, like the following:
public IQueryable<Derived> Derivations
{
get { return Bases.OfType<Derived>(); } // filter list on type
}
Calling this works perfectly, and I can see the SQL being correctly generated. The backing type is DataQuery<T>.
The problem comes in when I assigning this IEnumerable to a datasource (either a control or a BindingSource).
From what I can see, the DataQuery object is queried for an IListSource. And it happily supplies this. Then it proceeds to make a BindingList, which fails as the type parameter of the 2 arguments supplied (IEnumerable<Derived> and Table<Base>) does not match. It raises an exception of MissingMethod as the constructor cannot be found.
The simple workaround is just to call ToList() on the IQueryable<Derived> before assigning to the datasource and then it works, but this is quite tiring.
Any suggestions to handle this without 'loosing' the IQueryable?
Thanks
leppie
UPDATE:
The bug has now been reported to MS. More details here. Thanks Marc!
Confirmed. Looks like a bug to me; you should log it on Connect. The team are fixing LINQ-to-SQL bugs, so it might not be ignored. For now, use .ToList() etc.
Sample code:
using (var ctx = new MyDataContext())
{
var qry = ctx.BaseEntities.OfType<DerivedEntity>();
IListSource ls = (IListSource)qry;
IList list = ls.GetList(); // boom
/* Constructor on type
'System.Data.Linq.Provider.DataBindingList`1[snip]'
not found.*/
}
I had the same issue (still not fixed MS guys!).
To keep the IQueryable I did a .Cast<object>() when assigning to the datasource (i use it to output a xls file from any L2S table i want in a DynamicData website).

Categories