Problem using Where() for Azure Mobile Service - c#

I have the following test that doesn't work:
public class DesktopDTO
{
public DesktopDTO() {}
public DesktopDTO(string title, Guid otherId)
{
Id = Guid.NewGuid();
Title = title;
OtherId = otherId;
}
public Guid Id { get; set; }
public string Title { get; set; }
public Guid OtherId { get; set; }
}
//setup environment:
MobileServiceClient mobileService = new MobileServiceClient("http://myserver.azurewebsites.net/");
IMobileServiceSyncTable<DesktopDTO> table = mobileService.GetSyncTable<DesktopDTO>();
if (!mobileService.SyncContext.IsInitialized)
{
var store = new MobileServiceSQLiteStore("localstore1.db");
store.DefineTable<DesktopDTO>();
await mobileService.SyncContext.InitializeAsync(store);
}
DesktopDTO input = new DesktopDTO("test124", Guid.NewGuid()); //this is my entity
//invoke action:
await table.InsertAsync(input);
//check results:
List<DesktopDTO> all = await table.ToListAsync(); //this returns 1 item
DesktopDTO r1 = all.Where(x => x.Id == input.Id).FirstOrDefault(); //this returns the created item
var query12 = await table.Where(x => x.Title == "test124").ToCollectionAsync(); //this returns 1 item
DesktopDTO r = (await table.Where(x => x.Id == input.Id).ToCollectionAsync()).FirstOrDefault(); //this returns null!!
The problem is that the last local query, which uses a Where() clause filtered by Id (which is the PK of the DesktopDTO entity), doesn't return the wanted entity.
The entity has been correctly INSERTed in the DB (as the other queries show, even the one filtered by "Title"), so I don't understand why the Where() filter should not work only with the PK.
I also tried using the LookupAsync() method, but again I got no results.
What am I doing wrong?
Thank you!

I try to reproduce the issue on my side. But I got the ArgumentException: "The id must be of type string".
If I change the Id type from Guid to string, I can't reproduce the issue that you mentioned. I works correctly on my side.
public class DesktopDTO
{
public DesktopDTO() { }
public DesktopDTO(string title, Guid otherId)
{
Id = Guid.NewGuid().ToString();
Title = title;
OtherId = otherId;
}
public string Id { get; set; }
public string Title { get; set; }
public Guid OtherId { get; set; }
}
Test Result:

For future reference, I discovered the problem.
Azure Mobile Service doesn't allow to have (natively) fields like GUIDs. But it accept Guids and silently transform them into Strings, using UPPER CASE.
Therefore, the solution is to turn all the Guids into UPPER CASE in the queries.
You can do either:
DesktopDTO r = (await table.Where(x => x.Id.ToString.ToUpper() == input.Id.ToString.ToUpper()).ToCollectionAsync()).FirstOrDefault();
or directly:
DesktopDTO r = await table.LookupAsync(id.ToString().ToUpper());

Related

LINQ return type and use in the other function

I am trying to get a result from LINQ query and pass it to different function to use there.
This is my first function that called from browser. Then, GetAddressByIncIDis called.
// GET: Hello/5
public void GetModel(int? incID)
{
var incAddress = GetAddressByIncID(incID);
var siteID = GetSiteIDbyAddress(incAddress);
LoadSite(siteID);
}
private ?returntype? GetAddressByIncID(int? incID)
{
var incAddress = (from address in db.Incs
where address.IncID == incID
select new
{
IncID = address.IncID,
Number = address.Number,
Direction = address.Direction,
StreetName = address.StreetName,
Zip = address.Zip,
City = city.CityDesc,
State = state.StateDesc
}).FirstOrDefault();
return incAddress;
}
At this point, I could get a query result that I wanted. I only needed one or null so I set FirstOrDefault().
Within this function, I am able to access values inside of incAddress, like incAddress.IncID, and I want to do this in other function when it passed to them. Because its type is Anonymous Type, I am not sure what return type should I use.
Once this is returned, I pass it to other function that called GetSiteIDbyAddress to find out SiteID. This result may be multiple.
private ?returntype? GetSiteIDbyAddress(string incAddress)
{
var searchaddress = (from address in db.Addresses
where PrimaryAddressNumber == incAddress.Number && Direction == incAddress.Direction && StreetName == incAddress.StreetName
select new
{
SiteID = address.SiteID
});
return searchaddress;
}
Would you please give me a suggestion on both first and second function's return type? I searched and tried (list, array, etc) but could not find a great solution for it.
EDIT:
With suggestions, I have edited my code to looks below. Now I am able to use values from IncAddress to pass to the other function. However, the other problem arise.
public class IncAddress
{
public int IncID { get; set; }
public string Number { get; set; }
.
.
}
public class IncSite
{
public int? SiteID { get; set; }
.
.
}
private IncAddress GetAddressByIncID(int incID)
{
var incAddress = (from address in db.Incs
where address.IncID == incID
select new IncAddress
{
IncID = address.IncID,
Number = address.Number,
Direction = address.Direction,
StreetName = address.StreetName,
Zip = address.Zip,
City = city.CityDesc,
State = state.StateDesc
}).FirstOrDefault();
return incAddress;
}
private IncSite GetSiteIDbyAddress(IncidentAddress incidentAddress)
{
var searchaddress = (from address in db.Addresses
where PrimaryAddressNumber == incAddress.Number
select new IncSite
{
SiteID = address.SiteID
});
return searchaddress;
}
On GetSiteIDbyAddress, I get an error says:
Cannot implicitly convert type 'System.Linq.IQueryable <
HelloWorld.Controllers.HelloController.IncSite > ' to
'HelloWorld.Controllers.HelloController.IncSite'. An explicit
conversion exists (are you missing a cast?)
I think it is because I did not put FirstOrDefault() because I expect multiple record will be found by this query. How can I complete this function to get a multiple results?
You can do this in two ways, either by creating a new class Let it be some Address with those selected values as fields and in this case return type will respect to Address(Address if you use FirstOrDefault, List if you use .ToList() and so on). Or you can specify the return type as dynamic in this case no other changes is needed. But I suggest you to create a Class if possible and avoid the usage of dynamic in this case, since
it is a lot slower and won't have the same compile time error checks
as a proper class
updates as per David's comment
Case 1 :
//definition should be something like this
public class Address
{
public string IncID { get; set; }
public string Number { get; set; }
public string Direction { get; set; }
public string StreetName { get; set; }
public string Zip { get; set; }
public string City { get; set; }
public string State { get; set; }
}
And the Method should be like this:
private Address GetAddressByIncID(int? incID)
{
var incAddress = (from address in db.Incs
where address.IncID == incID
select new Address
{
IncID = address.IncID,
Number = address.Number,
Direction = address.Direction,
StreetName = address.StreetName,
Zip = address.Zip,
City = city.CityDesc,
State = state.StateDesc
}).FirstOrDefault();
return incAddress;
}
Case 2
private dynamic GetAddressByIncID(int? incID)
{
var incAddress = // Query here
return incAddress;
}

Get parent object plus its child count without anonymous type

So I have the next method (which works) to return a list of claims plus its observations. One claim can have zero-or-many observations. Code works but I'm afraid its a mess, with the anonymous type and then parsing it into a new Claim type, setting the count.
public async Task<IEnumerable<Claim>> GetClaims(ClaimStatusCode status, int take = 10, int skip = 0)
{
using (var db = new DataContext())
{
var pendingclaims = await (from claim in db.Claims
where claim.OfficeCode == _officeCode
where claim.ClaimStatusCode == status
select new
{
ID = claim.ID,
ClaimStatusCode = claim.ClaimStatusCode,
OpenDate = claim.OpenDate,
LastUpdateDate = claim.LastUpdateDate,
CloseDate = claim.CloseDate,
ProductCode = claim.ProductCode,
IssueCode = claim.IssueCode,
SpecificIssueCode = claim.SpecificIssueCode,
OfficeCode = claim.OfficeCode,
Summary = claim.Summary,
ObservationsCount = claim.Observations.Count
}).OrderBy(c => c.OpenDate).Take(take).Skip(skip).ToListAsync();
var list = new List<Claim>();
foreach (var claim in pendingclaims)
{
Claim c = new Claim()
{
ID = claim.ID,
ClaimStatusCode = claim.ClaimStatusCode,
OpenDate = claim.OpenDate,
LastUpdateDate = claim.LastUpdateDate,
CloseDate = claim.CloseDate,
ProductCode = claim.ProductCode,
IssueCode = claim.IssueCode,
SpecificIssueCode = claim.SpecificIssueCode,
OfficeCode = claim.OfficeCode,
Summary = claim.Summary,
ObservationsCount = claim.ObservationsCount
};
list.Add(c);
}
return list;
}
}
I think maybe I'm missing something to reduce the mess of the resulting SQL query, but don't figure what. Any idea?
UPDATE
As requested, here's the Claim and Observation class, I'm using a plain simple Entity Code First One to Many relationship:
Claim
public class Claim
{
public Claim()
{
Observations = new List<Observation>();
}
[Key]
public Guid ID { get; set; }
...
public virtual ICollection<Observation> Observations { get; set; }
[NotMapped]
public int ObservationsCount { get; set; }
}
Observation
public class Observation
{
public Observation()
{ }
[Key]
public Guid ID { get; set; }
...
public virtual Guid ClaimID { get; set; }
[ForeignKey("ClaimID")]
public virtual Claim Claim { get; set; }
}
There is no way in EF6 to get what you want without some intermediate projection (being it anonymous type or concrete type, as soon as it's not an entity type). But if you need all the object fields plus child count, you can simplify the implementation like this:
var pendingclaims = await (from claim in db.Claims.AsNoTracking()
where claim.OfficeCode == _officeCode
where claim.ClaimStatusCode == status
orderby claim.OpenDate
select new
{
claim,
ObservationsCount = claim.Observations.Count
}).Take(take).Skip(skip).ToListAsync();
return pendingclaims.Select(item =>
{
item.claim.ObservationsCount = item.ObservationsCount;
return item.claim;
}).ToList();

linq/ef: How to form the query which connects multiple tables to return result?

I'm extremely new to ASP .NET and LINQ so please forgive me for my ignorance.
I've a Region class:
public class Region
{
[Key]
public int Region_ID { get; set; }
public string Region_Name { get; set; }
}
And a Service class:
public class Service
{
[Key]
public int Service_ID { get; set; }
public string Service_Name { get; set; }
}
And a mapping class which stores the many-many mapping of service_IDs with region_IDs:
public class Mapping_ServiceToRegion
{
[Key]
public int Service_ID { get; set; }
public int Region_ID { get; set; }
}
Now I want to create an API function which outputs Region_Name based on given Service_ID. This is what I have so far in my RegionsController:
// GET api/Regions/Service_ID
[ResponseType(typeof(Region))]
public async Task<IHttpActionResult> GetRegion(int id)
{
var region_id = from sr in db.Mapping_ServiceToRegions
where sr.Service_ID == id
select sr.Region_ID;
var region = await db.Regions.Select(r =>
new Region()
{
Region_ID = r.Region_ID,
Region_Name = r.Region_Name
}).SingleOrDefaultAsync(r => r.Region_ID == region_id); //ERROR
if (region == null)
{
return NotFound();
}
return Ok(region);
}
The error I'm getting is:
Cannot convert lambda expression because it is not a delegate type.
I realize that my region_id variable will have multiple region_ids based on a service_id. How can I modify the code to account for this? Is there an IN operator that I can use to say r.Region_ID IN region_id?
And does the above code look correct otherwise?
Thanks.
You should change the SingleOrDefaultAsync() call using Contains() method like below since your region_id is of IEnumerable<T> and not a single value and so you can't perform direct equality comparison.
SingleOrDefaultAsync(r => region_id.Contains(r.Region_ID))
Ahh!!! here Region is one of EF mapped entity and you are trying to construct that and thus the error. You should either chose to select an Anonymous type (or) use a custom viewmodel/DTO object like
var region = await db.Regions.Select(r =>
new
{
Region_ID = r.Region_ID,
Region_Name = r.Region_Name
}).SingleOrDefaultAsync(r => region_id.Contains(r.Region_ID));

Unknown column in field list - After MySQL changes

I have a query which pulls some data after making a couple of joins, this worked fine when the application used SQL Server. However after making the transfer to MySQL I'm having some issues.
For example I keep getting the error 'Unknown column Extent.Group_ClientID'. I have identified the line at which this error occurs at but I don't understand why.
Entity:
[Table("tblsupplier")]
public partial class Supplier
{
[Key][Column(Order = 0)]
public int ClientID { get; set; }
[Key][Column(Order = 1)]
public int SupplierID { get; set; }
[StringLength(50)]
public string AccountNo { get; set; }
[StringLength(100)]
public string SupplierName { get; set; }
public string DisplayName {
get {
return this.SupplierName + " (" + this.AccountNo + ")";
}
}
public virtual Client tblClient { get; set; }
}
Query:
public IQueryable<Supplier> GetAllSuppliersByClientWithClaims(int ClientID, List<int> WrittenOffIDs) {
return (from s in alliance.Suppliers
where s.ClientID == ClientID
join h in alliance.Headers
on new { a = s.ClientID, b = s.SupplierID }
equals new { a = h.ClientID, b = h.SupplierID }
join d in alliance.Details
on new { h.ClientID, h.ClaimID }
equals new { d.ClientID, d.ClaimID }
join r in alliance.Reviews
on new { h.ClientID, h.ReviewID }
equals new { r.ClientID, r.ReviewID }
where r.ReviewPeriodID != 0
where d.SplitLine == false
where !WrittenOffIDs.Contains((int)d.WrittenOffID)
select s).Distinct().OrderBy(r => r.SupplierName);
}
Method:
public string GetSupplierAutoComplete(int ClientID) {
DashboardViewModel model = new DashboardViewModel();
GeneralMethods GeneralHelpers = new GeneralMethods(reviewPeriodRepo, supplierGroupRepo, detailRepo);
model.Suppliers = supplierRepo.GetAllSuppliersByClientWithClaims(ClientID, GeneralHelpers.GetWrittenOffCodes(ClientID));
//Fails here
return JsonConvert.SerializeObject(model.Suppliers.Select(r => r.DisplayName), Formatting.Indented);
}
However, I have done some playing around and I've found that one of the where's in the query is causing this issue. where d.SplitLine == false. Now in the database SplitLine is a Tinyint. As suggested because this is the boolean type for MySQL. Now if I pull a single 'SplitLine', it will return true or false based on the 0 or 1. Whereas if I use it in a where statement, it fails. Why it this?
UPDATE:
This only seems to happen when I enumerate the list
Found the answer! I'm not entirely sure why the error was occurring! Anyway..
I have two entities:
Header and Group.
I have a foreign key in my header entity which create a link with group. It said that one header can have one group. Which after I reviewed, was incorrect. One header can have many groups. So I changed the foreign key from this:
public virtual Group Group { get; set; }
To:
public ICollection<Group> Groups {get; set; }
This then worked. However, I haven't made a reference to group so I'm not sure to why this threw an error. If anyone knows, please let me know in the comments.

Linq "join" with a IList<T> getting "Error Unable to create a constant value.."

I have a Save Method that saves with a Linq query a manually re-orderd list (in a web form) that is passed as the parameter to my method, and I try to update the Order Property of the IEnumerable<VM_CategoryLabel> I retrieve from the database (EF) with the corresponding value in the list (maybe would that be clearer with my code below):
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
//Order = x.Sequence_Cat,
Order = (int)listTemplate.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
I used the "test" var to see if my "sub-query" gets the correct value, and it does, but when I use my Linq expression inside the Select (the commented Order line), I get the following error:
Unable to create a constant value of type 'Namespace.Models.VM_CategoryLabelExtra. "Only primitive types and enumeration types are supported in this context.
Here are my classes:
public class VM_CategoryLabel
{
public int Id { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
public class VM_CategoryLabelExtra
{
public int Id { get; set; }
public int IdCat { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
So I suppose that I should not query the list inside my query ? So how do I "match" the 2 lists of values ?
I also tried the following (after having replace in the Linq query: Order = x.Sequence_Cat)that is not working neither because the iteration variable is
read-only:
foreach (var item in requete)
{
item.Order = listTemplate.Where(x => x.Id == item.Id).Select(x => x.Order).FirstOrDefault();
}
try
{
context.SaveChanges();
I suggest using this.
It is the let clause.
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
let list = listTemplate
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
Order = list.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
edit: instead offrom you can just do let list = listTemplate
Should work now :)
example for let:
// The let keyword in query expressions comes in useful with subqueries: it lets
// you re-use the subquery in the projection:
from c in Customers
let highValuePurchases = c.Purchases.Where (p => p.Price > 1000)
where highValuePurchases.Any()
select new
{
c.Name,
highValuePurchases
}
If you do not know how Let working than please download LinqPad and see an example

Categories