I use Windows Azure Mobile Service.
I have a table of Element.
I want to query the cloud database :
select Id, Name
FROM Element ORDER BY creationTime
But I don't understand at all the "query" system with Windows Azure Mobile Service.
I have a IMobileServiceTable but don't know what to do with that...
I checked on tutorial, and they explain how to use Where clause, but not select. And I need to select only some column because my element have picture and I don't want to download it in my getAll method....
Edit :
I try that :
Task.Factory.StartNew(() =>
{
var query = table.Select(x =>
new Element()
{
Id = x.Id,
Name = x.Name,
Price = x.Price
});
var _items = query.ToListAsync().Result;
}).ContinueWith((x) => handleProductsArrived(x.Result));
But it doesn't work.
You can find a helpful post from Carlos that includes what the corresponding SQL query would be here: http://blogs.msdn.com/b/carlosfigueira/archive/2012/09/21/playing-with-the-query-object-in-read-operations-on-azure-mobile-services.aspx
For example:
function read(query, user, request) {
query.where({ UserId: user.userId })
.select('id', 'MovieName', 'MovieRating')
.orderBy('MovieName')
.take(10);
request.execute();
}
woudld translate to
SELECT TOP 10 [id], [MovieName], [MovieRating]
FROM MovieRating
WHERE Rating > 2 AND UserId = ?
ORDER BY MovieName
So for your case where you need to translate
SELECT Id, Name
FROM Element
ORDER BY creationTime
you'd go with something like the following:
function read(query, user, request) {
query.where({ UserId: user.userId })
.select('id', 'Name', 'Element')
.orderBy('creationTime')
request.execute();
}
It sounds like you are just looking to do a simple query with IMobileServiceTable
SELECT Id, Name FROM Element ORDER BY creationTime
If you do not mind using the IMobileServiceTable<TodoItem>, you can try:
1) Removing the member properties you do not need from your Object
Example:
public class TodoItem
{
public int Id { get; set; }
// REMOVE WHAT YOU DO NOT WANT
//[DataMember(Name = "text")]
//public string Text { get; set; }
[DataMember(Name = "complete")]
public bool Complete { get; set; }
}
2) Here's the code to read the data:
private void RefreshTodoItems()
{
items = todoTable
.OrderBy( todoItem => todoItem.Id )
.Take(10)
.ToCollectionView();
ListItems.ItemsSource = items;
}
which is basically:
SELECT TOP 10 Id, Complete FROM TodoTable ORDER BY Id
The code example for todoTable is at http://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-wp8/
Hope this helps.
If you're using .net, you pretty much follow linq.
Looking at the sample app - where it has -
private void RefreshTodoItems()
{
// This code refreshes the entries in the list view be querying the TodoItems table.
// The query excludes completed TodoItems
items = todoTable
.Where(todoItem => todoItem.Complete == false)
.ToCollectionView();
ListItems.ItemsSource = items;
}
If, for example, you did not want to return the Complete flag you could add before the call to .ToCollectionView()
.Select(item=>new {item.Id, item.Text})
Which would create a list of a new object of anonymous type (can be a concrete type) with the two members specified.
Related
I have two models:
public class Employee
{
public int Id { get; set; }
public IList<Skill> { get; set; }
}
public class Skill
{
public int Id { get; set; }
}
And I have filter with list of skill ids, that employee should contain:
public class Filter
{
public IList<int> SkillIds { get; set; }
}
I want to write query to get all employees, that have all skills from filter.
I tried:
query.Where(e => filter.SkillIds.All(id => e.Skills.Any(skill => skill.Id == id)));
And:
query = query.Where(e => e.Skills
.Select(x => x.Id)
.Intersect(filter.SkillIds)
.Count() == filter.SkillIds.Count);
But as a result I get exception says that query could not be translated.
It is going to be a difficult, if not impossible task, to run a query like this on the sql server side.
This is because to make this work on the SQL side, you would be grouping each set of employee skills into a single row which would need to have a new column for every skill listed in the skills table.
SQL server wasn't really made to handle grouping with an unknown set of columns passed into a query. Although this kind of query is technically possible, it's probably not very easy to do through a model binding framework like ef core.
It would be easier to do this on the .net side using something like:
var employees = _context.Employees.Include(x=>x.Skill).ToList();
var filter = someFilter;
var result = employees.Where(emp => filter.All(skillID=> emp.skills.Any(skill=>skill.ID == skillID))).ToList()
This solution works:
foreach (int skillId in filter.SkillIds)
{
query = query.Where(e => e.Skills.Any(skill => skill.Id == skillId));
}
I am not sure about it's perfomance, but works pretty fast with small amount of data.
I've also encountered this issue several times now, this is the query I've come up with that I found works best and does not result in an exception.
query.Where(e => e.Skills.Where(s => filter.SkillIds.Contains(s.Id)).Count() == filter.SkillIds.Count);
I have two lists.
Client List : one of them receive from user (client)
Server List : one of them get from database .
I want to do 3 work on this lists .
One : Add ( if data not exists in server List and exist in the client List )
Two : Remove data ( if exist in the server List and not exist in the client List )
Three : Update ( if data exist in server List and client Server and data has Changed )
This is my model:
public class CategoryPropertyDto
{
public Guid? Id { get; set; }
public Guid CategoryId { get; set; }
public string PropName { get; set; }
public CategoryPropertyType CategoryPropertyType { get; set; }
}
the client send me the list of this model CategoryPropertyDto.
Now I write this code for adding data:
var currentValue = getAllPropByCategory.Result.Select(x => new CategoryPropertyDto
{
CategoryId = x.CategoryId,
CategoryPropertyType = x.CategoryPropertyType.CategoryPropertyType,
Id = x.Id,
PropName = x.PropName
}).ToList();
/// Add New Property
var newProp = request.CategoryPropertyDtos.Where(x => x.Id == null).ToList();
List<CategoryProperty> CategoryProperty = new List<CategoryProperty>();
if (newProp.Count() > 0)
{
foreach (var item in newProp)
{
CategoryProperty.Add(new CategoryProperty(item.PropName, item.CategoryPropertyType, item.CategoryId));
}
await unitOfWork.CategoryRepository.CategoryPropertyRepository.AddBulkCategoryProperty(CategoryProperty, cancellationToken);
}
and its worked well.
Now I want to find items for remove (exist in the server List but not exist in the clientList)
var removeValue = currentValue.Except(request.CategoryPropertyDtos).ToList();
but it did not work and return all currentValue and I don't know how can I find the items for update.
How can I solve this problem?
var removeValue = currentValue.Except(request.CategoryPropertyDtos).ToList();
This line does not work because in this case Except method compare References, not Values and obviously currentValue items have different references than client items.
solution:
You have to use Id in all cases (add, delete, update) as you already did for Add case.
So for example to find items to delete:
var ItemIdsToDelete = currentValue.Select(p => p.Id).Except(request.CategoryPropertyDtos.Select(p => p.Id)).ToList();
and delete items by id using method like this:
await unitOfWork.CategoryRepository.CategoryPropertyRepository.DeleteBulkCategoryPropertyById(ItemIdsToDelete, cancellationToken);
and you can do similar thing to find items that their ids exist in both client and server lists and update them.
I am trying to get the latest contact with a given user, grouped by user:
public class ChatMessage
{
public string SentTo { get; set; }
public string SentFrom { get; set; }
public string MessageBody { get; set; }
public string SendDate { get; set; }
}
The user's contact info could either be in SentTo or SentFrom.
List<ChatMessage> ecml = new List<ChatMessage>();
var q = ecml.OrderByDescending(m => m.SendDate).First();
would give me the latest message, but I need the last message per user.
The closest solution I could find was LINQ Max Date with group by, but I cant seem to figure out the correct syntax. I would rather not create multiple List objects if I don't have to.
If the user's info is in SentTo, my info will be in SentFrom, and vice-versa, so I do have some way of checking where the user's data is.
Did I mention I was very new to LINQ? Any help would be greatly appreciated.
Since you need to interpret each record twice - i.e. as a SentTo and a SentFrom, the query becomes a bit tricky:
var res = ecml
.SelectMany(m => new[] {
new { User = m.SentFrom, m.SendDate }
, new { User = m.SentTo, m.SendDate }
})
.GroupBy(p => p.User)
.Select(g => new {
User = g.Key
, Last = g.OrderByDescending(m => m.SendDate).First()
});
The key trick is in SelectMany, which makes each ChatMessage item into two anonymous items - one that pairs up the SentFrom user with SendDate, and one that pairs up the SentTo user with the same date.
Once you have both records in an enumerable, the rest is straightforward: you group by the user, and then apply the query from your post to each group.
It should be pretty easy, look at this code:
string username = "John";
var q = ecml.Where(i=>i.SentFrom == username || i.SentTo == username).OrderByDescending(m => m.SendDate).First();
It simply filter your collection be choosing items which either SentFrom or SentTo is equal to username.
In my project I've implemented N-to-N relation between records using this tutorial on OrchardProject web-site. I have 2 parts: MaterialPart & CategoryPart and association record.
Material part
public class MaterialPartRecord : ContentPartRecord {
public MaterialPartRecord() {
Categories = new List<ContentMaterialCategoryRecord>();
}
}
public class MaterialPart : ContentPart<MaterialPartRecord> {
public IEnumerable<CategoryPartRecord> Categories {
get { return Record.Categories.Select(cmcr => cmcr.CategoryPartRecord); }
}
}
CategoryPartRecord
public class CategoryPartRecord : ContentPartRecord {
...
}
public class CategoryPart : ContentPart<CategoryPartRecord> {
...
}
association record:
public class ContentMaterialCategoryRecord {
public virtual int Id { get; set; }
public virtual MaterialPartRecord MaterialPartRecord { get; set; }
public virtual CategoryPartRecord CategoryPartRecord { get; set; }
}
Now I need to select MaterialItems which are linked to certain category. So far I have this method to extract them. It works but I'm not sure that it is correct way to do this.
public IEnumerable<MaterialPart> GetMaterialsByCategory(int catId) {
var cs = new CategoriesService(_oServices);
CategoryPartRecord cat = cs.GetItem(catId).Record;
return _oServices.ContentManager
.Query(VersionOptions.Latest, _contentType)
.Join<CommonPartRecord>()
.OrderByDescending(cpr => cpr.PublishedUtc);
.List()
.Where(ci => ci.IsPublished())
.Select(ci => ci.As<MaterialPart>())
.Where(mp => mp.Categories.Contains(cat)); // < ---- ?
}
So my question is: what is correct way to select materials for required category, which produces optimal SQL query, as we simply need to inner join associated record table with required CategoryPartRecord_Id field value.
thaks!
In case, of M : N with pairing object, we can use QueryOver and subquery. The biggest benefit would be, that we recieve the plain set of material Items, which we can use for paging (Take(), Skip())
var session = ... // get curretn session
CategoryPartRecord category = null;
ContentMaterialCategoryRecord pair = null;
MaterialPartRecord material = null;
var subquery = QueryOver.Of<ContentMaterialCategoryRecord>(() => pair)
// now we will join Categories to be able to filter whatever property
.JoinQueryOver(() => pair.CategoryPartRecord, () => category)
// here is the filter
// there could be IN, >= <= ...
.Where(() => category.ID == 1)
// or
.WhereRestrictionOn(c => c.category.ID).IsIn(new[] {1, 2, 3})
...
// now we will return IDs of the Material we are interested in
.Select(x => pair.MaterialPartRecord.Id);
// finally the clean query over the Materials...
var listOfUsers = session.QueryOver<MaterialPartRecord>(() => material )
.WithSubquery
.WhereProperty(() => material.Id)
.In(subquery)
// paging
.Take(10)
.Skip(10)
.List<MaterialPartRecord>();
So, this will produce the most effective SQL Script, with one subselect, and clean select from material table
NOTE: similar stuff could be done even with LINQ. But QueryOver is NHibernate most native way I'd say. Anyhow, the principe - subquery to filter by category, and main query to load materials will remain the same. Only ONE SQL Select call
I know it's pretty standard stuff but right know the solution escapes me. I have entity Documents. In my service I can call DocumentsRepository.All() and then use only what I need but I don't want to carry all the unneeded data. I guess I have to use anonymous object to achieve this, but the exact implementation escapes me.
In Documents entity I have column Id and column UserId. How can I write my LINQ to only get those two values?
P.S
And what type should I use for my method? Maybe object but I would like something more specific.
Building upon olivers answer, if you want to return that from a method, you could use dynamic:
public dynamic ReturnSomeData()
{
return context.Documents.Select(d => new
{
Id = d.Id,
UserId = d.UserId
});
}
You have to keep in mind that you trade compiler checking for flexibility.
This should work for what you need, if you want to put this into a method you should create a type that contains all the info you need.
var selectedItems = context.Documents.Select(d => new
{
Id = d.Id,
UserId = d.UserId
});
EDIT
Use in a method:
public class MyData
{
public int Id { get; set; }
public int UserId { get; set; }
}
public IEnumerable<MyData> GetMyDataFromDocuments()
{
return context.Documents.Select(d => new MyData
{
Id = d.Id,
UserId = d.UserId
});
}