OData V3 exclude property - c#

I wish to exclude a particular property from a entity collection over odata.
I've used .Expand("Files") to retrieve that particular collection, they are files in the database and I want all of the metadata (like Name and MimeType) of the file, but not the binary body itself.
I am not allowed to change the OData service itself so if it's possible at all, it must be done using a instruction in the odata query.
Any thoughts? Thx in advance.
UPDATE2: Vagif has been helpful, but made me realize I did not phrase my question entirely correctly. Once again: apologies. The actual property can be in the class that is returned by expanding "Files", but it must be null. In other words: I'd like the expanded child records to have a property not being filled with data.
UPDATE: Thx nemesv. I should indeed have been more specific. The odata service is build using the odata nuget package using visual studio using c#. The client uses the same tools. The server however uses odata 5.0.1. The client any version I want, (5.6 now I think).

If you use C# and LINQ on the client side, you can select the properties you want to include in the payload using Select clause and anonymous types, e.g.:
var results = context.MyCollection.Select(x => new { x.Name, x.Category });
UPDATE
The trick with selecting columns from expanded entity is that you don't need to explicitly expand it: WCF Data Services LINQ provider fill figure it out. Here's an example using Northwind data model:
[Test]
public void SelectProductAndCategoryColumns()
{
var result = _context.Products
.Where(x => x.ProductID == 1)
.Select(x => new { x.ProductID, x.Category.CategoryID, x.Category.CategoryName })
.Single();
Assert.AreEqual(1, result.ProductID);
Assert.AreNotEqual(0, result.CategoryID);
Assert.IsNotNull(0, result.CategoryName);
}
Note that I am chaining expanded columns in the Select clause without using Expand clause. And it works this way (but only for one level, you can't chain them further).

Related

What is the lambda expression for retrieving two grandchilden data for a parent table?

I'm trying to write an entity framework lambda expression to retrieve the related child data and two related grandchild data from two tables, plus a fourth child table. Here's the relationships.
The parent table is Class.
Class has a child table called Course.
Course has two child tables, CertificationLevel and CertificationType.
The fourth table is called ClassLocation, and is a child table of Class.
I've got to bring in all the related Course, CertificationLevel, CertificationType, and ClassLocation for all the retrieved Class data.
This is what I've currently got:
var recordList = ctx.Classes.OrderByDescending(r => r.ID).Include(c => c.Course).Include(cl => cl.ClassLocation).Take(300);
I searched around for help and found this website Include Multiple Levels, but it doesn't work. Here's the line that I'm having trouble with Include(i => i.Invoices.Select(it => it.Items)). When I try to modify my lambda expression, the Select method isn't recognized.
So, what's wrong and how do I correct it?
I'm using EF 6.4.4. We're using .NET 4.5.2
Going back to the Include Multiple Levels link I referenced, I thought I'd try the "Using String Path". That worked. So now I have this:
var recordList = ctx.Classes.OrderByDescending(r => r.ID).Include("Course.CertificationType").Include("Course.CertificationLevel").Include("Course.InstrumentModel").Include(cl => cl.ClassLocation).Take(PreselectThreshold);
I would have preferred that the lambda expression would have worked, but it didn't. I do have the System.Data.Entity included, but that made no difference. I'm still wondering if the problem has something to do with the fact that we're using .NET 4.5.2 for development. Anyway, for anyone else struggling with this, try using the string path approach.
In EF Core I would do something like this(not sure if it is of any help on EF 6.4.4):
var data = context.Class
.Include(c => c.Course)
.ThenInclude(c => c.CertificationLevel ))
.Include(c => c.Course)
.ThenInclude(c => c.CertificationType))
.Include(c=> c.ClassLocation)
.ToList();
This Select isn't recognized? That's strange. I never worked with EF6.0 but every information points it should work
https://learn.microsoft.com/en-us/ef/ef6/querying/related-data
recordList = ctx.Classes
.OrderByDescending(r => r.ID)
.Include(c => c.Course.Select(c=> c.CertificationLevel))
.Include(cl => cl.ClassLocation)
.Take(300);
There was some solution on the link I posted to use EFCore Style of ThenInclude installing NuGet "Install-Package ThenInclude.EF6". But you shouldn't need to go that far

Add sort on reflected navigation property

I'm using EF 6.0 .NET-Framework and MS SQL Sever and I have the follow situation: I have a dynamic data selection on a Navigation property from a given entity. This works so far OK. But: I like to add some sortings. But I cannot figure out how to make EF understand, that the sort shall be sent to the database instead sorting on client side afterwards.
The problem seems, that the data is requested from database when I retrieve the navigation property's value and not when I complete the command chain with the sort.
My code is like (simplyfied):
var dynamicRelatedEntityType = typeof(RelatedEntity);
using (var dbContext = new DBContext())
{
var orderByFunction = buildOrderByFunction(dynamicRelatedEntityType ); // this just builds a function for the order by ...
var masterEntity = dbContext.MasterEntity.first(x=> x.Whatever = true);
var navigationProperty = masterEntity.GetType().GetProperty(dynamicRelatedEntityType.Name);
var result = navigationProperty.GetValue(masterEntity).OrderBy(orderByFunction).ToList();
// result is OK, but sort wasn't sent to data base ... it was done by my program which is quite time expensive and silly too ...
}
So, how I can change this behaviour, any ideas?
Thank you in advance!
EDIT
The solution provided for this question solves to do dynamic predicates, but you cannot apply them if you still use navigationProperty.GetValue(masterEntity). In that case the EF will fire SQL immediatley without any order or where clause ...
Your database server is able to process TSQL statements only. Entity Framework (particularly the SQL Server pluging for Entity Framework) is capable of translating a small subset of C# expressions in a valid TSQL (in your case for an order by statement).
When your expression is too complex (for example invokes methods, alters the state) to be translanted into TSQL, Entity Framework will resort to an in memory operation.
If your are using .NET Core, you can use the following snipped while registering the content to spot all the "unsupported" statements that are executed in memory.
var builder = new DbContextOptionsBuilder<MyContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
// the following line is the one that prevents client side evaluation
builder.ConfigureWarnings(x => x.Throw(RelationalEventId.QueryClientEvaluationWarning));
Givem that, which is important to understand when a custom expression is involved, LINQ requires a static expression to infer the ordering. However, you can generate a dynamic expression as suggested by LINQ Dynamic Expression Generation. Although I never tried the described approach, it seems to me a viable way to achieve what you ask.

Passing a GetWhere query (Func<entityDTO,bool>) to a data layer method which needs a (Func<entity,bool>) parameter to work

I have the following method in a data access class which uses entity framework:
public static IEnumerable<entityType> GetWhere(Func<entityType, bool> wherePredicate)
{
using (DataEntities db = new DataEntities())
{
var query = (wherePredicate != null)
? db.Set<entityType>().Where(wherePredicate).ToList()
: db.Set<entityType>().ToList();
return query;
}
}
This works fine when I use the entities across all layers... however I am trying to move to using a DTO class and I would like to do something like the following:
public static IEnumerable<EntityTypeDTO> GetWhere(Func<EntityTypeDTO, bool> wherePredicate)
{
//call a method here which will convert Func<EntityTypeDTO,bool> to
// Func<EntityType,bool>
using (DataEntities db = new DataEntities())
{
var query = new List<EntityType>();
if (wherePredicate == null)
{
query = db.Set<EntityType>().ToList();
}
else
{
query = (wherePredicate != null)
? db.Set<EntityType>().Where(wherePredicate).AsQueryable<EntityType>().ToList()
: db.Set<EntityType>().ToList();
}
List<EntityTypeDTO> result = new List<EntityTypeDTO>();
foreach(EntityType item in query)
{
result.Add(item.ToDTO());
}
return result;
}
}
Essentially I want a method which will convert Func to Func.
I think I have to break down the Func into an expression tree and then rebuild it somehow in the entityType?
I want to do this to allow the Presentation Layer to just pass the Expression queries?
Am I missing something basic or is there an easier design pattern that can pass a query from a DTO to a data access class without knowing the details of the query?
I have tried making the DTO inherit from the entity which doesn't seem to work either?
If there is a better design pattern that I am missing I would love a pointer and I can investigate from there...
Firstly I would suggest that you put a querying layer of your own in front of Entity Framework rather than allowing any arbitrary Func to be passed in because it will be very easy in the future to pass a Func that Entity Framework can not translate into a SQL statement (it can only translate some expressions - the basics are fine but if your expression calls a C# method, for example, then Entity Framework will probably fail).
So your search layer could have classes that you build up as criteria (eg. a "ContainsName" search class or a "ProductHasId" class) that are then translated into expressions in your search layer. This separates your app entirely from the ORM, which means that ORM details (like the entities or like the limitations of what Funcs can and can't be translated) don't leak out. There's lots out there that's been written about this some of arrangement.
One final note, though, if you are working close to the ORM layer, Entity Framework is very clever and you could probably get a long way without trying to translate your Func<dto, bool> to a Func<entity, bool>. For example, in the below code, accessing "context.Products" returns a "DbSet" and calling Select on it returns an IQueryable and calling Where on that also returns an IQueryable. Entity Framework will translate all of that into a single SQL statement so it won't pull all other Products into memory and then filter the ID on that memory set, it will actually perform the filtering in SQL even though the filter is operating on a projected type (which is equivalent to the DTO in your case) and not the Entity Framework entity -
var results = context.Products
.Select(p => new { ID = p.ProductID, Name = p.ProductName })
.Where(p => p.ID < 10)
.ToList();
The SQL executed is:
SELECT
[Extent1].[ProductID] AS [ProductID],
[Extent1].[ProductName] AS [ProductName]
FROM [dbo].[Products] AS [Extent1]
WHERE [Extent1].[ProductID] < 10
So, if you changed your code to get something like..
return context.Products
.Map<Product, ProductDTO()>()
.Where(productDtoWherePredicate)
.ToList();
.. then you might be just fine with the Funcs that you already have. I presume that you already have some sort of mapping functions to get from EF entities to DTOs (but if not then you might want to look into AutoMapper to help you out - which has support for "projections", which are basically IQueryable maps).
I am going to put this up as an answer.Thanks to Dan for the quick answer. Looking at what you are saying I can write a query/filter set of classes. for example, take the following code:
GetProducts().GetProductsInCategory().GetProductsWithinPriceRange(minPrice, maxPrice);
This code would run like so: Get Products would get all products in the table and the remaining functions would filter the results. if all queries run like this it may put a significant load on the Data Access Layer/ DB Server Connections... not sure.
or
An Alternate I will work on also is:
If each function creates a Linq expression, I could combine them like this: How do I combine multiple linq queries into one results set?
this may allow me to do this in a manner where I can return the filtered results set from the database.
Either way I am marking this as answered. I will update when I have more details.

Linq to DocumentDb, where clause on child

In a project i'm currently working on, we have come to realise that we should not use DocumentDb collections as if they are the equivalent of a table in f.ex SQL Server. As a result, we are now persisting all of the entities, belonging to a single tenant in a single collection.
We already have lots of linq queries in our codebase which assume that each document type (aggregate root) is persisted in a dedicated collection. In an attempt to make the transition painless, i set out to refactor our data access object, so that its api continues to reason about aggregate roots, and deal with the single collection vs dedicated collections in it's implementation.
My approach is to wrap an aggregate root in an Resource<T> object, which derives from Resource and exposes a Model property as well as a Type property. I thought i would then be able to expose an IQueryable<T> to consuming code based on the following code:
return _client.CreateDocumentQuery<Resource<TModel>>(_collection.DocumentsLink)
.Where(x => x.Type == typeof(TModel).Name)
.Select(x => x.Model);
Initial testing showed that this worked as planned and i confidently committed my changes. When doing functional testing however, we found that some queried models had all of their properties set to their default values (ie. null, 0, false, etc).
I can reproduce the problem with the following code:
var wrong = _client.CreateDocumentQuery<Resource<TModel>>(_collection.DocumentsLink)
.Where(x => x.Type == typeof(TModel).Name)
.Select(x => x.Model)
.Where(x => !x.IsDeleted)
.ToArray();
var correct = _client.CreateDocumentQuery<Resource<TModel>>(_collection.DocumentsLink)
.Where(x => x.Type == typeof(TModel).Name)
.Where(x => !x.Model.IsDeleted)
.Select(x => x.Model)
.ToArray();
The results of the above queries are not the same!!
Both queries return the same number of TModel instances.
Only the instances returned by the second example have their properties populated.
In order for my refactoring to be successful, i need wrong to be ... right :) Falling back to SQL is not an option as we value type safety of linq. Changing our approach to expose the Resource<T> objects would touch lots of code, as it requires all *.Property references to be substituted by *.Model.Property references.
It seems an issue with the linq provider that is part of the DocumentDb client.
We use Microsoft.Azure.DocumentDb version 1.4.1
Edit 2015-09-24
The generated SQL queries are:
correct: {"query":"SELECT VALUE root.Model FROM root WHERE ((root.Type = \"DocumentType\") AND (NOT root.Model.IsDeleted)) "}
wrong: {"query":"SELECT * FROM root WHERE ((root.Type = \"DocumentType\") AND (NOT root.Model.IsDeleted)) "}
Also, this issue has been reported (and picked up) on GitHub here: https://github.com/Azure/azure-documentdb-net/issues/58
This has been confirmed as a problem with the SDK. a fix has been checked in and will ship with the next SDK drop.
in the interim you can use SQL, or change where you place the WHERE clauses.

The member 'IdLogement' is not supported in the 'Where' Azure Mobile Services query

I use Azure Mobile Service and I try to execute this query :
var interventions = await _interventionRemoteTable.Where(inter => inter.Logement.IdLogement == idLogement).ToListAsync();
An error occurs each time :
The member 'IdLogement' is not supported in the 'Where' Azure Mobile Services query
If I make this request in two steps (by getting all "Interventions" first and then filtering local), it works.
var interventions = await _interventionRemoteTable.ToListAsync();
var result = interventions.Where(inter => inter.Logement.IdLogement == idLogement);
Of course, this is not what I want. I want the filtering is done on the server in order to not get thousands of data.
Is there a method for filtering on a navigation property?
Thanks,
Mobile Services has a simplified model for querying, which means that you can't directly specify LINQ queries that are based off multiple tables.
I can think of two options to work around this:
Write a custom API where you pass in the parameter to filter on, IdLogement in this example.
Add a parameter to your GET table controller method to take IdLogement as a parameter, and have the server add an additional .Where clause to do the filtering.

Categories

Resources