How to add/edit DataServiceQuery LINQ Projections in Silverlight? - linq

Silverlight 5 / WCF Data Services 5.6.0 / Entity Framework 5 / Asynchronous LINQ DataServiceQuery
How can I reset/delete/uncache/remove the projections from my DataServiceQuery LINQ query in subsequent executions ?
Consider the below code:
var query =
(
from c in context.Customers
select new Customers()
{
ID = c.ID,
Name = c.Name,
}
) as DataServiceQuery<Customers>;
query.BeginExecute((result) =>
Dispatcher.BeginInvoke(() =>
{
// process results from query...
// query.EndExecute(result).ToList();
}), null);
In the above snippet, I'm creating a LINQ query with two projections (columns) which will return the ID and Name fields via WCF Data Services. This works fine; issue starts below...
In another method which executes later, I have a similar query to fetch additional columns/projections. However, the below LINQ query returns the same resultset as above and ignores the additional columns:
var query =
(
from c in context.Customers
select new Customers()
{
ID = c.ID,
Name = c.Name,
Age = c.Age, // additional columns returning null
Height = c.Height // additional columns returning null
}
) as DataServiceQuery<Customers>;
query.BeginExecute((result) =>
Dispatcher.BeginInvoke(() =>
{
// process results from query...
// query.EndExecute(result).ToList();
}), null);
Removing all projections to return all columns fails too; I just get back the initial ID and Name fields specified earlier:
var query =
(
// No projections, just get ALL columns please!
from c in context.Customers
select c
) as DataServiceQuery<Customers>;
query.BeginExecute((result) =>
Dispatcher.BeginInvoke(() =>
{
// process results from query...
// query.EndExecute(result).ToList();
}), null);
How can I get the DataServiceQuery object to discard the previously specified projections? I don't have the option to execute the query which returns all columns first.

Issue resolved: Before executing any LINQ query, set the below MergeOption on the WCF Data Service context:
context.MergeOption = MergeOption.OverwriteChanges;
Related info:
http://social.msdn.microsoft.com/Forums/silverlight/en-US/f36d2643-661e-4048-88cf-a38df36a0b1a/linq-expand-and-dataservicequery
WCF Data Service and Silverlight: DataServiceQuery<T> will not re-perform query

Related

How does one use .ToList() Inside of a Linq Query?

I've got a class that contains a list item. I would like for a linq query to populate the class, including this list. Here is my query:
var query = from c in context.Cars
select new CarListItem()
{
ID = c.ID,
Make = c.Make,
AvailableColors = context.CarColors.Where(u => u.CarID == c.ID).ToList()
};
Basically, I want to get a list of all of the cars, including a list of the available colors for each respective car.
The problem is that the inclusion of .ToList() within the query results in an error: An error occurred:
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[CarSystem.Models.CarColors] ToList[CarColors](System.Collections.Generic.IEnumerable`1[CarSystem.Models.CarColors])' method, and this method cannot be translated into a store expression.
At this point, I don't know whether I am just using wrong syntax within the Linq query (should I use something other than .ToList()?) or if maybe the architecture of the models is wrong.
You can't. EF tries to translate ToList() to SQL and doesn't know how.
You could project to another type, then call ToList():
var query = (from c in context.Cars
select new
{
ID = c.ID,
Make = c.Make,
AvailableColors = context.CarColors.Where(u => u.CarID == c.ID)
}).ToList()
.Select(c => new CarListItem()
{
ID = c.ID,
Make = c.Make,
AvailableColors = c.AvailableColors.ToList()
});
or change the type of CarListItem.AvailableColors to IEnumerable<CarColor>:
var query = from c in context.Cars
select new CarListItem()
{
ID = c.ID,
Make = c.Make,
AvailableColors = context.CarColors.Where(u => u.CarID == c.ID)
};

Returning an odata IQueryable object that differs to the query options

I need to get the following code to work
public IQueryable<BankingDTO> Get(ODataQueryOptions<TillSummaryDTO> options)
{
return((IQueryable<BankingDTO>)options.ApplyTo(this._bankingService.GetBanking()));
}
I would like to query on TillSummaryDTO because it has the field "TillOpID" on it. However I would like to return BankingDTO as this is the end result which contains the group by and sum. When I run the query I receive the error "Cannot apply ODataQueryOptions of 'Bepoz.Presentation.ViewModels.TillSummaryDTO' to IQueryable of 'Bepoz.Presentation.ViewModels.BankingDTO" what is the best practice for this?
The bankingservice.GetBanking method looks like this
var query = from t in _tillSummaryRepository.Table
join w in _workStationRepository.Table on t.TillOpID equals w.WorkstationID
join s in _storeRepository.Table on w.StoreID equals s.StoreID
join v in _venueRepository.Table on s.VenueID equals v.VenueID
select new TillSummaryDTO
{
TillOpID = t.TillOpID,
Cash = t.Cash,
Workstation = new WorkstationDTO()
{
WorkstationID = w.WorkstationID,
Name = w.Name,
Store = new StoreDTO()
{
StoreID = s.StoreID,
StoreGroup = s.StoreGroup,
Name = s.Name,
Venue = new VenueDTO()
{
VenueID = v.VenueID,
VenueGroup = v.VenueGroup,
Name = v.Name,
}
}
}
};
return query.GroupBy(x => x.Workstation.Name)
.Select(x => new BankingDTO()
{
TotalCash = x.Sum(y => y.Cash),
WorkstationName = x.Key
});
The scenario you want to achieve is that you have an entity set of TillSummaryDTO that you want to query, and you would like the return type to be a collection of BankingDTO. The query for the BankingDTO is carried out by applying the query options in the URL onto TillSummaryDTO . But the fact that the BankingDTO and TillSummaryDTO are different kind of types makes it impossible achieve that in a simple Get action method, right?
This scenario can be better resolved by the function feature of the OData protocol that the function takes the TillSummaryDTO collection as input parameter, has some internal complicated logic to query for the right BankingDTO, and returns the BankingDTO instead of TillSummaryDTO.
For the concept of function in OData protocol, you can refer to this link for V4 and section "10.4.2. Functions" of this page for V3.
For implementation, this sample can be referred to for Web API OData V4 and this tutorial can be referred to for Web API OData V3.

Entity Framework query: object not set to an instance of an object

When calling the query.ToList() I get
object reference not set to an instance of an object
For x.Gallons, all the orders have this value set, it's not null. Also, there are 2 DProducts in the database table with proper ID. What could be wrong?
ProductSummaryCollection.Clear();
var query = from p in Repository.repository.ctx.DProduct
join fo in Repository.repository.ctx.DFuelOrder.Include("DProduct")
on p.ID equals fo.DProductID
group fo by fo.DProduct into Prod
select new DProductSummary
{
Product = fo.DProduct,
TotalGallons = (float)Prod.Sum(x => x.Gallons)
};
try
{
IList<DProductSummary> ps = query.ToList();
foreach (DProductSummary dps in ps)
ProductSummaryCollection.Add(dps);
}
catch (Exception exc)
{
}
It seems that you can't do the following 2 things:
Create a entity object, DProduct in my case inside a linq query
You cannot access a reference in a linq query even if you Include it
So you have to use the join table.Propery instead.
A working query:
var query = from fo in Repository.repository.ctx.DFuelOrder.Include("DProduct")
join p in Repository.repository.ctx.DProduct
on fo.DProductID equals p.ID
group fo by new { fo.DProductID, p.Name } into Prod
select new DProductSummary
{
ProductName = Prod.Key.Name,
TotalGallons = (float)Prod.Sum(x => x.Gallons)
};

LINQ to Entities does not recognize the method 'Boolean CheckMeetingSettings(Int64, Int64)' method

I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.

generic method to search for an LINQ entity based on its type

partial void UpdateDenomLimit(DenomLimit instance)
{
var oldData = DataClassesDataContext.DenomLimits.Where(b => b.ID == instance.ID).First();
//Code that logs the audit when instance and oldData is passed to it
LogAudit(oldData, instance);
//Code that updates the instance
this.ExecuteDynamicUpdate(instance);
}
Above is a method for updating an instance of DenomLimit in the database.
I am logging an audit of what changes are done to the entity. For that I get the previous state of the instance by the following code and it works fine:
var oldData = DataClassesDataContext.DenomLimits.Where(b => b.ID == instance.ID).First();
now I need a generic LINQ query which can fetch the oldData when three parameters are passed to it:
1. an instance of any type
2. A primary key column name
3. A value of primary key column for the passed instance.
...so that I can keep that code in the LogAudit and then do not need to fetch in every function.
A LINQ query which will possibly look like:
var oldData = DataClassesDataContext.GetTable<instance>().Where("b => b." + colName + " == #0", new object[] { id }).First();
The full namespace is used here to indicate the namespace that it came from to make it easier to incorporate into your code.
public T GetFirstOrDefault<T>(System.Linq.Expressions.Expression<Func<T, bool>> func) where T : class
{
DataClassesDataContext.ObjectContext.CreateObjectSet<T>().FirstOrDefault(func);
// For EF 4.1 Code First
//return (ctx as System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext.CreateObjectSet<T>().FirstOrDefault(func);
// or
//return ctx.Set<T>().FirstOrDefault(func);
}
In this case using an Expression of a Func allows EF to figure out what data you are looking for.
Here is an example of how it would be used:
var a1 = GetFirstOrDefault<DenomLimits>(p => p.ID == oldData.ID);
var a2 = GetFirstOrDefault<DenomLimits>(p => p.OtherID == 5);

Resources