Select Single Value with ADO.Net Data Services and LINQ - c#

Trying my hand at ADO.Net data services. All the examples shows how to retrieve lists but how would you go about retrieving a single value? e.g. Product X's Price.
Here is the LINQ query i use:
var qry = (from p in
svcContext.Products
where p.ProductName == "Chair"
&& p.Colour == 1
select c) as DataServiceQuery;
Product returnedProd;
qry.BeginExecute(
(pr) => returnedProd = qry.EndExecute(pr).First(), null);
Here i try to retrieve the product and load it into a local variable, but the local var stays null.
Pretty sure, i'm doing it completely wrong :)...any help would be greatly appreciated.

It's not suppose to be
var qry = (from p in svcContext.Products where p.ProductName == "Chair" && p.Colour == 1 select p) where did you declare the c ?

Sorry was supposed to be
var qry = (from p in
svcContext.Products where
p.ProductName == "Chair" && p.Colour
== 1 select p) as DataServiceQuery< Product >;

First() should throw an exception if the result set is empty - are you sure the query is even executing?

You are not the first to get hit by the asynchronous nature of all silverlight outgoing requests.
In the lambda expression
(pr) => returnedProd = qry.EndExecute(pr).First()
you capture the local variable returnedProd but usually the thread that will spin off AFTER BeginExecute has been called will be too late. It will probably executed after the execution goes out of scope of the current method and the variable will be lost.
The solution is to use effectively the "returnedProd" to populate the UI or whatever you need to do IN the lambda expression. Something like :
(pr) => {
returnedProd = qry.EndExecute(pr).First();
MessageBox.Show("Retrieved record" + returnedProd.Id);
}
Otherwise useful answer for the community, I wish I had one a few weeks ago :(

Related

DefaultIfEmpty() + select new MyStronglyTypeObj()

my aim is to return result via left join by linq. The io.IsDefault can be null but insted of this I want to return MyStronglyTypeObj obj with the rest data.
context.Image.Where(i => i.IsActive == true) have 3 rows. one of those have isDefault null because this ImageId- (io => io.ImageId == i.ImageId) dosent exist in ImageObject
var test2 = (from i in context.Image.Where(i => i.IsActive == true)
from io in ImageObject.Where(io => io.ImageId == i.ImageId).DefaultIfEmpty()
select new MyStronglyTypeObj() { Alt = i.Alt, Caption = i.Caption, DisplayName = i.DisplayName, Extension = i.Extension, IsDefault = io.IsDefault, Height = i.Height, Width = i.Width, Name = i.Name });
// return 2 imgs - the 3rd one without isDefault (isDefault = null) wasn't added to collection.
var test = (from i in context.Image.Where(i => i.IsActive == true)
from io in ImageObject.Where(io => io.ImageId == i.ImageId).DefaultIfEmpty()
select i); // return 3 imgs
Is something obvious to me that I don't see? - perhaps I totally misunderstood the .DefaultIfEmpty() function
please help
DefaultIfEmpty() only affects empty collections, and causes that collection to return a single element with value default(T) (where T == collection type).
For example, using strings (note default(string) == null):
So based on the code you provided:
DefaultIfEmpty() is not a factor
The only other difference is the select statement, which doesn't really make sense
I'm guessing i is type MyStronglyTypeObj (based on properties matching)? I suspect there's another factor when you're running this code that you're not taking into account.
Try putting a breakpoint on that line, and viewing the results in the debugger.
Also, because LINQ uses deferred execution, this query code doesn't actually "run" until it gets consumed, and depending on when that happens, the source data can change (essentially, easily causing timing bugs if you're changing the source data somewhere else). Even more frustrating, this can cause this bug to disappear when you use a debugger and view the results in that, as it causes the code to execute sooner. You can avoid this by adding a .ToList() at the end of the line to cause the results to be executed immediately.

Checking the contents of a LINQ query that is being added to

I'm creating a LINQ query that needs to check on the contents before adding something from the list, so what I have is this
var foo = (from f in list1
from p in list1.list2
from m in p.Bar
let t = m.Type
let c = someMethod(t)
where c.Type == type && !foo.Contains(p)
select p).ToList();
the !foo.Contains(p) is not allowed, so is there a way of checking the query as it goes along or before the ToList() should I just add Distinct() to do the same as the condition?
There is no way to access the query as it is being built in the manner you are doing. If you want to ensure that a particular value only appears once in the output then Distinct is the best approach

Combining LINQ queries in LINQ to CRM causes problems?

Something weird is going on.
If I do this:
var allAccountsQuery = from acc in baseQ
where
//high potential check - 1, 2, 3
(acc.mcpl_potencjal_klienta == 1 || acc.mcpl_potencjal_klienta == 2 || acc.mcpl_potencjal_klienta == 3) &&
//directors block check
((acc.mcpl_blokada_dyrektorska == true && acc.mcpl_blokada_do <= date) || acc.mcpl_blokada_dyrektorska == false || acc.mcpl_blokada_dyrektorska == null) &&
//sack assign date check
(acc.mcpl_dataprzypisaniazworka == null || acc.mcpl_dataprzypisaniazworka < date) &&
//owner change check
(acc.mcpl_datazmianywasciciela == null || acc.mcpl_datazmianywasciciela < date) &&
//creation date check
//TODO:For testing!
//(acc.mcpl_data_utworzenia_test < date)
(acc.createdon < date)
select acc;
var joinQuery = from acc in allAccountsQuery
join opp in ctx.opportunityopportunities on acc.accountid equals opp.customerid.Value
select new
{
Account = acc,
Opportunity = opp
};
Plugins.Common.XrmHelper.ClearCache("account");
var joinResult = joinQuery.ToList();
Then I'll get an unknown platform error when executing this query. I need to copy-paste the WHOLE where clause from allAccountsQuery to the joinQuery and use baseQ again, and then it works.
What's going on here? I thought you can safely join LINQ queries as long as you're not doing any unsupported operations.
PS. The STRANGEST part is that the pasted code WILL work with slightly different where conditions.
PPS. baseQ is just an even simpler where query, much like the allAccountsQuery.
Maybe is not the answer but as I can't leave a comment and no one has answer I think this could help.
Why you don't do the join in the first query? As from I know the LINQ CRM queries have problems joining tables when in the clause WHERE we have the OR Predicate, and not when we try to select from different tables, I think for you query should work. I have one post explaining what I learned.
Linq-to-CRM has a limited set of supported operations compared to other providers life EF or Linq-to-SQL.
You may have better success hydrating one or both of the two queries. Since your account query has a where clause try hydrating it:
var joinQuery = from acc in allAccountsQuery.ToList() // call ToList() to hydrate the query
join opp in ctx.opportunityopportunities
on acc.accountid equals opp.customerid.Value
select new
{
Account = acc,
Opportunity = opp
};
If you have a LARGE number of Opportunities you may want to try and filter that query based on the accounts returned from the first query before doing the Join.

Calling functions from within linq statement

Just wondering if this is the most efficient method of doing this? is there a way of having all the linq within one statement instead of calling a method, like a subselect or something?
newEmployee = (from emp
in db.employees
select new
{
a.EmployeeID,
a.Username,
Status = emp.GetEmployeeCurrentStatus(a.Username)
}).ToList();
This is the GetEmployeeCurrentStatus which returns the status of the employee:
public string GetEmployeeCurrentStatus(string username)
{
using (Entities db = new Entities())
{
var times = (from d in db.TimeTables
where d.DateTime == DateTime.Today &&
d.Employee.Username == username
select d)
.OrderByDescending(d => d.TimeID).FirstOrDefault();
return (x.ClockOut == null ? "IN" : "OUT");
}
}
how about:
newEmployee = (db.employees.Select(emp => new
{
emp.EmployeeID,
emp.Username,
Status = db.TimeTables
.Where(d => d.Employee.Username == emp.Username
&& d.DateTime == DateTime.Today)
.Select(x => x.ClockOut == null ? "IN" : "OUT")
.FirstOrDefault()
})).ToList();
Your attempt may appear cleaner and is functionally ok. However, it is firing up a secondary db call. This will be bad for scalability and performance. The version i've posted uses the same initial db connection and will make the join 1-1. This will result in tighter, faster queries as well as lower resource usage.
You cannot really call a custom method inside a query (or a part of a query that will be executed using the databse). You have essentially two options:
Call ToList before performing the select that needs to call the method (this way, the method will be called on in-memory data)
Compose the query such that it can all run on the SQL server if it is possible. This can be done using AsExpandable extension in predicate builder. For more information on how this works, see also my blog post.
its fine for small data (employees count) but since each GetEmployeeCurrentStatus requires an sql new connection so its not that best practice.
I personally will get all employees (one trip to database) and then get all employees status (one trip to database) so i cashed them all, now i'll join them locally
Hope this helped
Regardless of efficiency, having GetEmployeeCurrentStatus(...) as a method makes the code clearer and more reusable.
Assuming you are using LINQ to SQL or EF, I would refactor your query to use a Join. That way, you will execute a single efficient SQL query on the database, instead of two separate queries.

Linq, should I join those two queries together?

I have a Logins table which records when user is login, logout or loginFailed and its timestamp. Now I want to get the list of loginFailed after last login and the loginFailed happened within 24 hrs.
What I am doing now is get the last login timestamp first. then use second query to get the final list. do you think I should join those two queries together? Why not? Why yes? And how if yes?
var lastLoginTime = (from inRecord in db.Logins
where inRecord.Users.UserId == userId
&& inRecord.Action == "I"
orderby inRecord.Timestamp descending
select inRecord.Timestamp).Take(1);
if (lastLoginTime.Count() == 1)
{
DateTime lastInTime = (DateTime)lastLoginTime.First();
DateTime since = DateTime.Now.AddHours(-24);
String actionStr = "F";
var records = from record in db.Logins
where record.Users.UserId == userId
&& record.Timestamp >= since
&& record.Action == actionStr
&& record.Timestamp > lastInTime
orderby record.Timestamp
select record;
}
In the long run, I don't think it'd matter. No matter how you actually build the query in LINQ to SQL, the ultimate sequence of events on the DB server will be
get lastInTime
use lastInTime as part of records filter
Now... doing it as part of a single query will save on roundtrips of the actual date-time, so you can get some performance that way. But I would suggest that you only try to merge them if you absolutely need to because your performance profiling suggested that query was a bottleneck.
I don't think you should combine them because your current queries are quite readable. I think if they were combined it would be more difficult to understand the code.
I wouldn't merge, for reasons already stated by everyone else, but you can simplify the first query a bit: instead of
orderby inRecord.Timestamp descending
select inRecord.Timestamp).Take(1);
you can simply say:
select inRecord.Timestamp).Max();
It'll do the same thing, but it's a bit clearer than your way.
You can also use the IQueryable objects to compose more complex queries and still keep the code pretty easy to read. (I mixed the Extension syntax and query syntax just to show it can be done. You can just as easily swap this code around to separate it out as you would any other code in your solution.)
var usersRecords = db.Logins.Where(r => r.Users.UserId == userId);
var userLoginTimes = usersRecords.Where(r => r.Action == "I")
.Select(r => r.Timestamp);
var usersFunctions = usersRecords.Where(r => r.Action == "F");
var records = from record in usersFunctions
where userLoginTimes.Any()
let lastLoginTime = userLoginTimes.Max()
where record.Timestamp >= since
&& record.Timestamp > lastLoginTime
select record;

Categories