Converting from Sql to Linq - asp.net

I have, what I thought was a pretty straight-forward query.
In normal Sql this would read:
SELECT [column names]
FROM agentscheduledetail
WHERE (date = '2012-07-04') AND
(
exception = 'Break (No Sign Off)' OR
exception = 'Break' OR
exception = 'Break (Signed Out)'
)
This returns approx 900 records.
However, when I try to enter this into my controller, I end up with around 300,000 records - so I think my AND and ORs are not working. I've tried Linqer, but can't get it to work (I'm aware this may not be actual LINQ but the equivalent query in VS - if there is a linq version... I'd be grateful for that too if possible).
My controller query is:
var dte = DateTime.Today;
return View(db.agentscheduledetails.Where
(
d => d.date == dte && d.agentName.StartsWith("ta") &&
(
d.exception == "Break (No Sign Off)" ||
d.exception == "Break" ||
d.exception == "Break (Signed Out)"
)
).ToList()
);
Can anyone either a) let me know where I'm going wrong with my && || (and/or), or b) is there a way of stepping through the code in VS, to see what the above query translates to in normal SQL so I can try to figure out where I'm going wrong?
Thanks for any help,
Mark

The following is perhaps a simplified version of what you are trying to do, also your LINQ contains an additional statement compared to the SQL where it is comparing the agent name?
var currentDate = DateTime.Today;
var exceptionTypes = new List<string>() { "Break (No Sign Off)",
"Break", "Break (Signed Out)" };
db.agentscheduledetails.Where(d => d.date == currentDate &&
exceptionTypes.Contains(d.exception));
One thing that you could try is getting hold of a copy of LinqPad, this will let you run your LINQ statement against a database and will show you what the generated SQL statement is.

Aside from anything else,
d.agentName.StartsWith("ta")
does not appear in your original sql...?

Related

LINQ to Entities does not recognize the method call within it

I completely understand that this is because LINQ query requires the whole expression to be translated to a server , and therefore I cant call an outside method in it. but as I have looked at other answers there is not relative solution to this. the only approach that I thought about is to loop through all the items in the model and than passing them to the query one by one but even though this approach would not help so I am seeking help in here for anyone to help me to figure out how to call a method or a way of calling a method appendstr that initializes a.PostedDate before checking its actual equivalent value in the giving LINQ Query.
[HttpGet]
public ActionResult SearchResult(int? page, string searchTitle = null, string searchLocation = null, string last24 = "")
{
ViewBag.searchTitle = searchTitle;
ViewBag.searchLocation = searchLocation;
ViewBag.page = page;
ViewBag.last24 = last24;
setUpApi(searchTitle, searchLocation);
var result = new List<AllJobModel>().AsQueryable();
if (!string.IsNullOrEmpty(ViewBag.searchTitle) || !string.IsNullOrEmpty(ViewBag.searchTitle) || !string.IsNullOrEmpty(ViewBag.last24))
{
setUpApi(searchTitle, searchLocation);
DateTime now = DateTime.Now;
result = db.AllJobModel.Where(a => a.JobTitle.Contains(searchTitle) && a.locationName.Contains(searchLocation) &&
appendstr(a.PostedDate).Equals(now.AddHours(-24).ToString("MM-dd-yyyy")));
}
else
{
result = from app in db.AllJobModel select app;
}
return View(result.ToList().ToPagedList(page ?? 1, 5));
}
The second method that gets called in the LINQ Query
public string appendstr(string str)
{
var x = str.Split(' ');
return 01 + "-" + x[1] + "-" + x[2];
}
I think you already understand that the .NET code you write in the Where clause is actually an expression that is parsed and converted to SQL. So if you have a funky string manipulation method, you can't use it directly.
The brute force option, as you seem to already understand, it to materialize the query first and then run the C# code over the results. You can do this with ToList() or AsEnumerable().
result = db.AllJobModel
.Where
(
a => a.JobTitle.Contains(searchTitle)
&& a.LocationName.Contains(searchLocation)
)
.AsEnumerable()
.Where
(
a => appendstr(a.PostedDate).Equals(now.AddHours(-24).ToString("MM-dd-yyyy")))
);
However in your specific case you can try a trick. You are attempting a date comparison, which SQL is perfectly capable of doing... you just need to convert that funky PostedDate to a SQL DateTime so that you can compare it directly. The gimmick for that is to use SqlFunctions.DateAdd to add null interval (e.g. 0 days). This implicitly converts the string to DateTime, where you can now query on the SQL side:
var targetDate = DateTime.Now.AddHours(-24);
result = db.AllJobModel
.Where
(
a => a.JobTitle.Contains(searchTitle)
&& a.LocationName.Contains(searchLocation)
&& SqlFunctions.DateAdd("DAY", 0, a.PostedDate) == targetDate
);
Credit goes to this post for the workaround.

How to get count of certain record saved in Uppercase, lower or a combination using LINQ query

I need to get the count of IN / in / In from SoccerStatus regardless if the records are saved in upper case, lower case or a combination of both ie IN or in or In from SQLite database in Xamarin Forms. How can I achieve that ?
var count_in = (from x in conn.Table<SoccerAvailability>().Where(x => x.SoccerStatus == IN) select x).Count();
Use string.Equals and tell it to ignore case...
var count_in = (from x in conn.Table<SoccerAvailability>().Where(x => string.Equals(x.SoccerStatus, "IN", StringComparison.OrdinalIgnoreCase)) select x).Count();
EDIT: Per your comment I see that the Linq provider you're using doesn't support string.Equals. You can try the following which should be more portable but possibly a bit slower...
var count_in = (from x in conn.Table<SoccerAvailability>().Where(x => x.SoccerStatus.ToUpper() == "IN") select x).Count();

Equivalent IN operator in entity framework

var query = from c in context.Albums
where c.AlbumID in albumIds
select c.Albumname;
Here albumIds is IENUM<> of some custom type(in my case its an Entity)
When I do the above query I get an error
Operator == cannot be applied to type int and IEnumerable
This error is acceptable but how do I overcome this
Turn it around slightly - you want to check whether your enumerable of album ids contains the id of an album you have just iterated to in your linq. So something like this ...
albumIds = {1,2,13,25,277,567};
var query = context.Albums.Where(x=> albumIds.Contains(x.ID));
(Sorry, writing example code without a tool in front of me so forgive any obvious mistakes. Hopefully you can get the idea from this though).
var albumIds= new string[] { "900", "801", "802", "803","906" };
var lstData = context.tbl.Where(
x => (x.TimeCreated >= yesterday && x.TimeCreated < today) &&
albumIds.Contains(x.TransactionSetId)
).Select(x => x.X12_Interchange).ToList();

Why is my query so slow?

I try to tune my query but I have no idea what I can change:
A screenshot of both tables: http://abload.de/image.php?img=1plkyg.jpg
The relation is: 1 UserPM (a Private Message) has 1 Sender (User, SenderID -> User.SenderID) and 1 Recipient (User, RecipientID -> User.UserID) and 1 User has X UserPMs as Recipient and X UserPMs as Sender.
The intial load takes around 200ms, it only takes the first 20 rows and display them. After this is displayed a JavaScript PageMethod gets the GetAllPMsAsReciepient method and loads the rest of the data
this GetAllPMsAsReciepient method takes around 4.5 to 5.0 seconds each time to run on around 250 rows
My code:
public static List<UserPM> GetAllPMsAsReciepient(Guid userID)
{
using (RPGDataContext dc = new RPGDataContext())
{
DateTime dt = DateTime.Now;
DataLoadOptions options = new DataLoadOptions();
//options.LoadWith<UserPM>(a => a.User);
options.LoadWith<UserPM>(a => a.User1);
dc.LoadOptions = options;
List<UserPM> pm = (
from a in dc.UserPMs
where a.RecieverID == userID
&& !a.IsDeletedRec
orderby a.Timestamp descending select a
).ToList();
TimeSpan ts = DateTime.Now - dt;
System.Diagnostics.Debug.WriteLine(ts.Seconds + "." + ts.Milliseconds);
return pm;
}
}
I have no idea how to tune this Query, I mean 250 PMs are nothing at all, on other inboxes on other websites I got around 5000 or something and it doesn't need a single second to load...
I try to set Indexes on Timestamp to reduce the Orderby time but nothing happend so far.
Any ideas here?
EDIT
I try to reproduce it on LinqPad:
Without the DataLoadOptions, in LinqPad the query needs 300ms, with DataLoadOptions around 1 Second.
So, that means:
I could save around 60% of the time, If I can avoid to load the User-table within this query, but how?
Why Linqpad needs only 1 second on the same connection, from the same computer, where my code is need 4.5-5.0 seconds?
Here is the execution plan: http://abload.de/image.php?img=54rjwq.jpg
Here is the SQL Linqpad gives me:
SELECT [t0].[PMID], [t0].[Text], [t0].[RecieverID], [t0].[SenderID], [t0].[Title], [t0].[Timestamp], [t0].[IsDeletedRec], [t0].[IsRead], [t0].[IsDeletedSender], [t0].[IsAnswered], [t1].[UserID], [t1].[Username], [t1].[Password], [t1].[Email], [t1].[RegisterDate], [t1].[LastLogin], [t1].[RegisterIP], [t1].[RefreshPing], [t1].[Admin], [t1].[IsDeleted], [t1].[DeletedFrom], [t1].[IsBanned], [t1].[BannedReason], [t1].[BannedFrom], [t1].[BannedAt], [t1].[NowPlay], [t1].[AcceptAGB], [t1].[AcceptRules], [t1].[MainProfile], [t1].[SetShowHTMLEditorInRPGPosts], [t1].[Age], [t1].[SetIsAgePublic], [t1].[City], [t1].[SetIsCityShown], [t1].[Verified], [t1].[Design], [t1].[SetRPGCountPublic], [t1].[SetLastLoginPublic], [t1].[SetRegisterDatePublic], [t1].[SetGBActive], [t1].[Gender], [t1].[IsGenderVisible], [t1].[OnlinelistHidden], [t1].[Birthday], [t1].[SetIsMenuHideable], [t1].[SetColorButtons], [t1].[SetIsAboutMePublic], [t1].[Name], [t1].[SetIsNamePublic], [t1].[ContactAnimexx], [t1].[ContactRPGLand], [t1].[ContactSkype], [t1].[ContactICQ], [t1].[ContactDeviantArt], [t1].[ContactFacebook], [t1].[ContactTwitter], [t1].[ContactTumblr], [t1].[IsContactAnimexxPublic], [t1].[IsContactRPGLandPublic], [t1].[IsContactSkypePublic], [t1].[IsContactICQPublic], [t1].[IsContactDeviantArtPublic], [t1].[IsContactFacebookPublic], [t1].[IsContactTwitterPublic], [t1].[IsContactTumblrPublic], [t1].[IsAdult], [t1].[IsShoutboxVisible], [t1].[Notification], [t1].[ShowTutorial], [t1].[MainProfilePreview], [t1].[SetSound], [t1].[EmailNotification], [t1].[UsernameOld], [t1].[UsernameChangeDate]
FROM [UserPM] AS [t0]
INNER JOIN [User] AS [t1] ON [t1].[UserID] = [t0].[RecieverID]
WHERE ([t0].[RecieverID] = #p0) AND (NOT ([t0].[IsDeletedRec] = 1))
ORDER BY [t0].[Timestamp] DESC
If you want to get rid of the LoadWith, you can select your field explicitly :
public static List<Tuple<UserPM, User> > GetAllPMsAsReciepient(Guid userID)
{
using (var dataContext = new RPGDataContext())
{
return (
from a in dataContext.UserPMs
where a.RecieverID == userID
&& !a.IsDeletedRec
orderby a.Timestamp descending
select Tuple.Create(a, a.User1)
).ToList();
}
}
I found a solution:
At first it seems that with the DataLoadOptions is something not okay, at second its not clever to load a table with 30 Coloumns when you only need 1.
To Solve this, I make a view which covers all nececeery fields and of course the join.
It reduces the time from 5.0 seconds to 230ms!

Linq To Sql Get data into Label

I have a label to show BookName. I get it from table which name tblBooks. I don't know how to show Book Name into the label.
var query = from b in dc.tblBooks.Where(b=>b.BookID == 'B01') select b;
Can you help me know.
Your query as written will return a collection of books—IQueryable<Book>. If you're sure there will only be one result in this query, you can call SingleOrDefault, which will execute the query immediately and give you the actual book.
var Book = dc.tblBooks.Where(b => b.BookID == 'B01').SingleOrDefault();
if (Book != null)
myLabel.Text = Book.BookName;
Or you can simply say:
var Book = dc.tblBooks.SingleOrDefault(b => b.BookID == 'B01');
Which does the same thing.
If you're 110% sure there will always be a result, and you don't want to check for null, then you can use Single, which will do the same thing, except throw an exception if no results are found, where SingleOrDefault simple returns null.
var Book = dc.tblBooks.Single(b=>b.BookID == 'B01');
myLabel.Text = Book.BookName;
Try:
label.Text = query.FirstOrDefault().BookName;

Resources