Error on Linq To Entities method contains NotSupportedException - c#

I've checked other questions and I believe this should work:
private static List<Duck> GetDucks(this DataServiceContext ctx, params int[] ids)
{
return ctx.CreateQuery<Duck>("Ducks").Where(x => ids.Contains(x.DuckID)).ToList();
}
I got a NotSupportedException: The method 'Contains' is not supported.
I changed the query to
Where(x => ids.Any(id => id == x.DuckID)
I received the message: The method 'Any' is not supported when MaxProtocolVersion is less than '3.0'
I then constructed the DataServiceContext a different way:
public static DataServiceContext Context
{
get { return new DataServiceContext(baseUri, DataServiceProtocolVersion.V3); }
}
I then received the message: NotSupportedException: The source parameter for the 'Any' method has to be either a navigation or a collection property
I think I could do with some advice before going any further.
EDIT
I did use MaxProtocolVersion 3 both client and server side.
In any case, this particular error message has gone away for now as I am no longer trying to return a subset of data. I simply get the full list of entities up front to work with (though this will need further consideration/optimization). I mistakenly believed that if I had a temp DataServiceContext that was empty I would have to retrieve the entities I wanted to update and then save them back (influenced by various examples on performing updates I found). Of course the method AttachTo(EntitySetName, entity) is the correct solution. Then you can call UpdateObject(entity) prior to TempContext.SaveChanges().

From what I have learned it looks like LINQ support in this scenario is limited, though I still do not know why contains does not work on a DataServiceQuery. The MSDN documentation suggests it is supported. Queries to the DataServiceContext resolve urls in OData format:
http://services.odata.org/OData/OData.svc/Category(1)/Products?$top=2&$orderby=name
The solution I used - adding a WCF Data Service operation/method:
[WebGet]
public IQueryable<Thing> GetThingsByID(string ids)
{
return CurrentDataSource.Things.Where(x => ids.Contains(x.ThingID.ToString())).AsQueryable();
}
The above should allow me to filter:
WCF Data Service operations
Calling Service Operations
I pass in ids as CSVs as only primitive data types may be passed as parameters
Note:
Contains works on the DBContext.

Related

Use dynamic parameters for RavenDB query

I am trying to simplify and compact my code, and eliminate code duplication as much as possible. I have a method that queries RavenDB collections, and the query needs to adapt to the type I am going to query. This type changes according to the parameters passed to the method, and the where clause also needs to adapt.
I have a base type, AdministrativeArea, which other types derive from (Level1_AdministrativeAreas to Level5_AdministrativeAreas). Depending on the scenario, I need to query AdministrativeAreas, Level1_AdministrativeAreas, etc.
What I currently have:
private void Merge(MergeLevel currentMergeLevel, IDocumentSession currentSession)
{
(...)
IQueryable<AdministrativeArea> query;
if (currentMergeLevel == MergeLevel.Level1)
query = currentSession.Query<AdministrativeArea, AdminAreaName>()
.Where(area => !string.IsNullOrEmpty(area.NAME_0) && !string.IsNullOrEmpty(area.NAME_1));
(...)
}
Is there any way to pass in the types as a method parameter and have them applied to the query, like this:
private void Merge(MergeLevel currentMergeLevel, IDocumentSession currentSession, Type requiredType, Type indexType)
{
(...)
IQueryable<requiredType> query;
if (currentMergeLevel == MergeLevel.Level1)
query = currentSession.Query<requiredType, indexType>()
.Where(area => !string.IsNullOrEmpty(area.NAME_0) && !string.IsNullOrEmpty(area.NAME_1));
(...)
}
I have faced several problems at compile time, namely "is a variable but is used like a type", and the fact that member variables (NAME_0, NAME_1, etc.) can't be inferred because the compiler doesn't know "what's coming".
I suspect this simply can't be done; however, this has implications for code maintenance, as I'll have to create different methods for each type of query OR create one rather large method. Neither of which are too appealing, but I don't see any way around that.
A good way of filtering by type would be to include Raven-Entity-Name field in the "select" clause of an index.
Then you would be able to filter types by using EntityType field.
You can see an example of this kind of index in the built-in Raven/DocumentsByEntityName index
So, your index might look like this:
from doc in docs
let entityType = doc["#metadata"]["Raven-Entity-Name"]
where entityType.EndsWith("_AdministrativeAreas")
select new
{
EntityType = entityType,
//the rest of the fields
}
Note that this would work if you are inserting documents through existing client API (raw REST API won't add Raven-Entity-Name on it's own)

Using POST or GET for a WebAPI Action method that returns a list, but requires params

Currently I have the following standard functions in each of my controllers to handle basic CRUD operations:
GET /api/todo Get all to-do items
GET /api/todo/{id} Get an item by ID
POST /api/todo Add a new item
PUT /api/todo/{id} Update an existing item
DELETE /api/todo/{id} Delete an item
However, the time came where I realized I actually need to pass multiple parameters to get a list of todo items that is filtered at the database level rather than retrieving all of the items and using linq.
For example here is how I decided to go about it:
In my Controller:
// POST: api/todo
[HttpPost]
public IList<TodoItem> Get([FromBody]GetTodoItemsRequest request)
{
return _todoItemManager.GetTodoItems(request.Name, request.CategoryId);
}
As you can see I created a new Model called GetTodoItemsRequest which will have a property for each of my parameters. In this case: Name, CategoryId.
I figured when dealing with multiple parameters and retrieving a list it is best to do POST and create a model specifically for it. Rather than using a GET and passing all kinds of information in the url.
It seems a bit strange to be doing the above... Would msot see it as a perfectly fine solution or is there something I am missing in the WebAPI world?
I believe that is semantically incorrect to use POST method for a simple read operation, even if you need a complex model. You are doing a pure query on your resource called todo, and this should really be a GET operation for many reasons:
It should be cachable: POST request aren't cachable by their nature, and caching is an important constraint in RESTful services.
It should semantically indicate that no side-effect will be raised from the call: GET requests must be idempotent and safe, POST operations, instead, indicate some kind of data manipulation. Your operation (filtering) is both idempotent and safe, so it should be spontaneously represented by a GET request.
The part of the URI after a ? character is called query string for a reason: it represent parameters that further specify the scope of a request. Well, isn't filtering results just an example of this approach?
Apart from that, it seems to me that, if Name and CategoryId are required parameters for your query, your filtering operation could be better represented by another URI in which Name and CategoryId are turned into route parameters:
http://yourhost.com/api/users/{name}/categories/{categoryId}/todos
Assuming a relationship between your name parameter (a user name maybe?) and the categories.
If, instead, your parameters are completely optional, then leaving them as query string parameters is the best choice:
http://yourhost.com/api/todos?name=nameValue&categoryId=categoryIdValue
A side note:
you should really use plural for your resources if they represents a collection of items: e.g. api/todo will return an array of todos, so you should rename it into api/todos.

Use EF Code First Local extention

The following method works properly in my service layer:
public override IList<City> GetAll()
{
var query = from item in _tEntities
select item;
query.Load();
return _tEntities.Local;
}
but when i try to run following method, the method returns old data loaded by GetAll() method.
public override IList<City> GetAll(Func<City, bool> predicate)
{
var query = from item in _tEntities
select item;
query.Where<City>(predicate);
query.Load();
return _tEntities.Local;
}
What is the problem and how can i fix them?
How can i use local method in this sample and reload new data into local(cache)?
You are looking at the wrong problem. What you are most likely seeing is a result of the fact that when you do the first query, the local cache is empty. So it only returns the results from your query. But when you do the second, it's returning the results of your first query AND your second query.
This comes down to the fact that you are using a shared DbContext between all your methods. Local contains a cache of all records the context has retrieved, not just the most recent query.
The correct solution is to not use Local in this manner. Even better, don't use a shared context since this can lead to context cache bloat.
I'm not too sure what you are trying to achieve with a .Load method here but it seems like you want the following.
public override IList<City> GetAll(Func<City, bool> predicate)
{
return _tEntities.Where<City>(predicate).ToList();
}
query.Where<City>(predicate);
This doesn't change query. The query.Load() on the next line ignores the predicate: you're calling query.Load() and not query.Where<City>(predicate).Load(). It's as if you had written
int i = 3;
i + 1;
Console.WriteLine(i); // still prints 3
In that example, C# does not really actually allow an addition to be used as a statement, but .Where(predicate) is a method call, and method calls can be used as such, even if they return values.
This is not your only issue (see the other answers), but my guess is that this issue is the one that leads to the unexpected results you're seeing.

DbSortClause expressions must have a type that is order comparable parameter Name :Key

I am using Linq to entity and have the following query
IQueryable<DomainModel.User> userResult =
userResult.OrderBy(u => u.UserClientRoles.OrderBy(r => r.Role.RoleName));
But I am getting this error
DbSortClause expressions must have a type that is order comparable
parameter Name :Key
and it returns an empty collection.
Any idea what's going on?
.OrderBy(), when working with databases, is supposed to take in a delegate that returns only a single property that represents a column in your database. I'm not sure what you're trying to do, but it looks like
u.UserClientRoles.OrderBy(r => r.Role.RoleName)
Will return an enumeration of values, which can't be ordered.
I had the same problem, I solved it using this:
your code:
IQueryable<DomainModel.User> userResult = userResult.OrderBy(u => u.UserClientRoles.OrderBy(r => r.Role.RoleName));
my code:
List<Membership> results = new List<Membership>();
results.AddRange(memberships.OrderBy(m => m.Roles));
memberships = results.AsQueryable();
coincidences:
*.OrderBy(m => m.Roles)
solution:
*.OrderBy(m => m.Roles.Select(r => r.RoleId).FirstOrDefault())
possible problem's reason:
Maybe, you did what I did, and cause that 1 user/member could have more than 1 role in the same membership. That made a conflict with/to OrderBy() because the application can just "order" a single element at the time, when she call the Role (which is an ICollection of elements) the instead receive more than 1 element with no kind of priority's levels (even when we could assume that the application will take the role's index as priority's base level, actually its don't).
solution's explaination:
When you add the *.Select(r => r.RoleId), you are specifying to the application which element will be used to OrderBy(). But, as you shall see when you maybe reached at this point, just by using the *.Select(r => r.RoleId) could be not enough, because the application is still receiving multiple results with the same priority's level. Adding *.Select(r => r.RoleId).FirstOrDefault() you are basically saying: "...I don't care how many results you received from that element, just the focus on the first result, or order them by its default..." (default normally means EMPTY or NULL).
additional information:
I used non-official's simple concepts/meanings to explain a complex solution with simple words, which means that you could maybe have problems to find similar posts in the web by using the words/concepts used in this "answer". Otherwise, the code itself works and you shouldn't not have any problem by applying it and/or modifying it by yourself. GOOD LUCK!!! (^_^)
In my case, I was accidentally trying to order by an object instead of ordering by one of it's properties.
You should you use
var query = from Foo in Bar
orderby Foo.PropertyName
select Foo;
Instead of
var query = from Foo in Bar
orderby Foo
select Foo;
Note: you will get the same behaviour event if there is an override on Foo's ToString() method.

ASP.NET MVC3: Reusable method which returns Database query results

I want a method where I can call, that would query the database for my given query string. Which can be refferenced from different controllers/actions. (note, complexity of actual query is quite big).
So a few questions:
Where would the code go. A new controller? Helper?
How would I reference it (call it)?
What object, if following my current style, would be the return type.
public Something WebCostCentres()
{
using (var db = Database.OpenConnectionString(Mynamespace.Properties.Settings.Default.dbConnString,
"System.Data.SqlClient"))
{
//ViewBag.CostCentres = db.Query("SELECT DISTINCT CostCentreNo");
return db.Query("SELECT DISTINCT CostCentreNo");
}
}
I would create some kind of Service class for this.
You crate the service and call the method.
The same type as your Query method. IEnumerable<Something> would be an option. You might have to call ToList or ToArray to execute the query, because the connection might be closed.
The service layer is often called repository. Google for this and you will find tons of examples.
1.Where would the code go. A new controller? Helper?
A class. Service oriented architecure wise.
2.How would I reference it (call it)?
As local variable in the page, filled via your trusted IOC container.
3.What object, if following my current style, would be the return type
None. Our current style is outdated. Ever heard of LINQ? The IQueryable extensions to .NET? It is not like they are new. It should return either IEnumerable or IQueryable, and be in general generic. Or a specific type in case only one number is returned.
repository pattern is applicable too
See an example
http://mstecharchitect.blogspot.com/2009/08/aspnet-mvc-and-linq-to-sql-using.html

Categories

Resources