Methods executing out of order when using Linq Select - c#

I have a series of methods that do some insertions into the database in a pretty set order. However, I'm running into an issue where one of the methods is firing off before the other method is called, unless I put a breakpoint in.
My initial thought was that due to me passing around IEnumerables, the method wasn't firing off until the Controller was returning the object, but after converting everything to lists, the error still occurs.
My code looks something like the following:
// controller
public IActionResult CreateConversation([FromBody] CreateConvoRequest model)
{
var userId = ViewBag.UserData.UserID;
model.message.UserFrom = userId;
model.users.Add(userId);
var result = MessagingLogic.CreateConversation(model.conversation, model.message, model.users);
return Json(result);
}
// Logic
public static Conversation CreateConversation(Conversation conversation, ConversationMessage message,
List<int> users)
{
conversation.DateCreated = DateTime.Now;
conversation.LastUpdated = DateTime.Now;
var convo = SaveNewConversation(conversation, users);
message.ConversationId = convo.ConversationId;
SendMessage(message);
return convo;
}
public static Conversation SaveNewConversation(Conversation conversation, List<int> users)
{
conversation = SaveConversation(conversation);
conversation.Users = users.Select(n => CreateConversationUser(conversation.ConversationId, n)).ToList();
// the above linq executes some SQL insertions and returns the new object
return conversation;
}
public static ConversationMessage SendMessage(ConversationMessage message)
{
if (message.CloseConversation) CloseConversation(message.ConversationId);
return SaveMessage(message);
}
What appears to be happening is that SendMessage is being called before CreateConversationUser. This is turn causes my messages not to be saved for the users as they aren't saved into the database until after the SendMessage method is called.
However, if I put a breakpoint on SaveNewConversation method, everything works as intended.
Edit
So after some more tinkering, adding the ToList to my Linq Selected corrected the issue with it executing out of order. However, my SendMessage SQL statement is using a INSERT INTO SELECT. It appears that the users often haven't been inserted into the database by the time the SendMessage sql executes.
For example, 2 of my users were inserted at 2017-09-20 10:29:35.820 and 2017-09-20 10:29:35.823 respectively. However, the message was inserted at 2017-09-20 10:29:35.810.
It appears something odd is happening on the SQL server.
Edit 2
Further testing is putting the entirety of the blame on SQL Server. I ran the SQL Profiler tool and the calls are coming in, in the correct order. However, there is only roughly a 3 millisecond delay between the last user being inserted and the messages being inserted.
If I place a sleep statement between the user insert and the message insert of a couple hundred milliseconds, it works as intended. I'm not quite sure how to address this issue, but it no longer appears to be executing out of order.

Related

Broken Skip method in let subquery

In my application there is an issue part that allows questions and responses. Something like (Pseudo code, as this is actually generated from Entity Framework):
class Response
{
string Author;
string Comment;
DateTime Date;
}
class Issue
{
IEnumerable<Response> Responses;
}
We have a summary page where we just want to show the last two responses. I tried a linq query like this:
from issue in db.Issue
let responses = from response in issue.Responses orderby response.Date
select new
{
Issue = issue,
Question = responses.FirstOrDefault(),
Answer = responses.Skip(1).FirstOrDefault()
}
But this gives me the error that Skip can only be used on ordered collections. I checked responses and it was an IOrderedEnummerable. I thought maybe the problem was that it was Enumerable instead of IOrderedQueryable, and saw that this happened because issue.Response is a collection so I switched the let statement to be:
let response = from response in db.Responses where response.IssueId = issue.ID // etc.
but this did not resolve the issue (but response did become an IOrderedQueryable) so I'm not really sure why entity won't accept the skip here. If I put the skip in the let statement, it works without problem (but then I can't get the first response). The issue seams to only occur by trying to put a portion of this statement in a variable before using it.
The problem here is: how will/should EF translate your query into a SQL statement? There is no straightforward SQL equivalent of Skip(1). Just try to write your query in SQL and you should see what I mean.
If you want an "easy" solution, then just get all responses from the DB and identify the ones you need in code.
If you want to minimize the data being read from the DB, the solutions might range from creating a view to writing a stored procedure to changing your tables so that your tables better reflect the data model in the application.
I'm not quite sure what's going on here, but wouldn't this be maybe a little simpler:
var theResponse = db.Issue.Select(i => new {
Issue = i,
Question = i.Responses.FirstOrDefault(),
Answer = i.Responses.OrderBy(r => r.Date).Skip(1).FirstOrDefault()
});
But this is also weird, because you are getting a full Issue object with all of its properties and whatnot, including the Reponse objects and stuffing it back into an Issue property of your dynamic type beside all of the Response objects...

Raven query only works sometimes

We've been having a problem where the following method which queries a raven db works, but only about 90% of the time
member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
to counteract this, I made this ugly hack workaround which does seem to have fixed the problem:
member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
System.Threading.Thread.Sleep(1000);
if (member.UserId.IsNullOrEmpty())
{
logger.Error("the userid was not loaded in time");
for (int i = 0; i < 5; i++)
{
member.UserId = userService.GivenUsernameGetUserId(command.EmailAddress.ToLower());
System.Threading.Thread.Sleep(1000);
if (member.UserId.IsNotNullOrEmpty())
{
logger.Info("The userid was retrieved in a loop after some delay ");
break;
}
}
if (member.UserId.IsNullOrEmpty())
{
logger.Error("a loop of 5 cycles was run trying to retrieve the userId but couldn't get it.");
}
}
Can anyone see why it might only be retrieving the correct data sometimes and whether there's a more elegant solution to making sure it keeps trying until it retrieves the data? I'm thinking whether there's some basic timeout setting that can be set in web.config or something?
The issue is likely stale indexes: the user has been recently created, and the indexes haven't had a chance to update. (Usually this takes milliseconds, but on a large database it can take longer.)
There are 3 things you can do here to fix your problem:
Option 1: Make User Ids based on email address. Then you don't have to mess with indexes at all.
Option 2: You can leave user IDs as-is, but wait for non-stale indexes.
Option 3: When you create a user, wait for indexes to update.
I'll describe each of these options below:
Option 1:
Make your user IDs well-known, so that you don't have to user indexes at all.
Say your object is called User. When you register the User, your code will look like:
public void RegisterUser(string emailAddress)
{
var user = new User
{
UserName = emailAddress,
...
};
// Give the User a well-known ID, so that we don't have to mess with indexes later.
user.Id = "Users/" + emailAddress;
ravenSession.Store(user);
}
If you do that, you won't have to mess with indexes at all. When it comes time to load your user:
public string GivenUsernameGetUserId(string userName)
{
// Look ma, no query needed.
return "Users/" + userName;
// Or, need to return the User itself? You can use .Load, which will never be stale.
// return ravenSession.Load<User>("Users/" + userName);
}
This is really your best option, and you never have to deal with indexes, therefore, you'll never have to deal with stale data.
Option 2
Option 2 is to use .WaitForNonStaleResults. It waits for indexes to become up-to-date before returning results.
public string GivenUsernameGetUserId(string userName)
{
// Use .WaitForNonStaleResultsAsOfNow()
return ravenSession.Query<User>()
.Customize(x => x.WaitForNonStaleResultsAsOfNow())
.Where(u => u.UserName == userName)
.Select(u => u.Id)
.FirstOrDefault();
}
Option 3
Option 3 is to wait for indexes to update when saving your user.
This requires Raven 3.5 or greater.
public void RegisterUser(string userName)
{
ravenSession.Advanced.WaitForIndexesAfterSaveChanges(timeout: TimeSpan.FromSeconds(30));
var user = new User {...};
ravenSession.Store(user);
ravenSession.SaveChanges(); // This won't return until the User is stored *AND* the indexes are updated.
};
Personally, I'd recommend using #1: well-known IDs for your users. Also, I recommend #3 even if you implement other solutions: SaveChanges will wait for the indexes to be updated before returning. This will result in fewer surprises around stale indexes, so I recommend it generally.
I am using Raven DB 3.5 and using mentioned Option 3
This does work but I encountered a problem using this approach:
In a specific use case this operation would take about 60 seconds to complete.
I'd not recommend to make general use of WaitForIndexesAfterSaveChanges() as it obviously can lead to tremendous performance issues.
Instead I'd configure queries using WaitForNonStaleResultsAsOfNow().

Why do I have to invoke function DataContext ExecuteMethodCall twice

I am using linq to sql to execute SPs, but getting this issue for a few of the SPs. I have to execute the function ExecuteMethodCall twice to get the result. The first call does not even go to SQL Server. I used SQL Profiler to check if the SP was executed, but I get this in profiler:
RPC:Completed exec sp_reset_connection
The subsequent call to ExecuteMethodCall actually executes the SP and gets data.
[Function(Name="dbo.Example_SP_To_Get_List")]
[ResultType(typeof(Example_SP_To_Get_List_getResult1))]
[ResultType(typeof(Example_SP_To_Get_List_getResult2))]
public IMultipleResults Example_SP_To_Get_List([Parameter(Name="SEARCH", DbType="VarChar(50)")] string sEARCH, [Parameter(Name="PAGE", DbType="VarChar(1)")] string pAGE, [Parameter(Name="ACTIVEFILTER", DbType="VarChar(4)")] string aCTIVEFILTER)
{
//first invocation, returns nothing, does not even call the SP (SQL Profiler does not show the sp call)
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), sEARCH, pAGE, aCTIVEFILTER);
//second time, it executes the SP and returns the result
result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), sEARCH, pAGE, aCTIVEFILTER);
return ((IMultipleResults)(result.ReturnValue));
}
Please help me find why this is happening to few SPs, while others work fine.
If a SP returns multiple result sets, then the caller function must fetch all result sets in sequence. In above case, the SP returned two result sets and I was only fetching the second result set.
Using following code resolves the issue:
var spResults = dbLayer.Example_SP_To_Get_List();
var result1 = spResults.GetResult<Example_SP_To_Get_List_getResult1>().ToList();
var result2 = spResults.GetResult<Example_SP_To_Get_List_getResult2>().ToList();
Not sure if it is the best way, but it resolved my issue.

Action already returned but browser not fetching results

In ASP.NET MVC 4, I have a [HttpGet] action method, that returns a JsonResponse.
public JsonResult List(int domainId)
{
....
}
When I call this method with the browser, by typing the URL: localhost:43229/GroupsAjax/List?domainId=1, I have to wait like 50 seconds to see the results.
My first thought was that the method was too slow, but by adding breakpoints I noticed that it already had returned.
I tried with Iexplorer too and the same lag happend.
In chrome, the lag was associated with the "Waiting time", according to the timeline
What might be happening?
I'm reviewing old questions (by me) without answers, the answer to this is/was:
I should have included the AsJson() code:
public dynamic AsJson()
{
return new
{
name = this.Name,
membersCount = this.Members.Count()
}
}
The query was this:
dynamic results = from g in groups.ToList()
select g.AsJson();
The "this.Members.Count()" is run in memory because of the ToList().
So: the query is divided in to parts: SQL To Entities and SQL To Objects
The first part was fast, the second was slow (has to do Members.Count() N times, being N the number of results in "groups.ToList()"

LINQ to Entities: Query not working with certain parameter value

I have a very strange problem with a LINQ to Entities query with EF1.
I have a method with a simple query:
public DateTime GetLastSuccessfulRun(string job)
{
var entities = GetEntities();
var query = from jr in entities.JOBRUNS
where jr.JOB_NAME == job && jr.JOB_INFO == "SUCCESS"
orderby jr.JOB_END descending
select jr.JOB_END;
var result = query.ToList().FirstOrDefault();
return result.HasValue ? result.Value : default(DateTime);
}
The method GetEntities returns an instance of a class that is derived from System.Data.Objects.ObjectContext and has automatically been created by the EF designer when I imported the schema of the database.
The query worked just fine for the last 15 or 16 months. And it still runs fine on our test system. In the live system however, there is a strange problem: Depending on the value of the parameter job, it returns the correct results or an empty result set, although there is data it should return.
Anyone ever had a strange case like that? Any ideas what could be the problem?
Some more info:
The database we query against is a Oracle 10g, we are using an enhanced version of the OracleEFProvider v0.2a.
The SQl statement that is returned by ToTraceString works just fine when executed directly via SQL Developer, even with the same parameter that is causing the problem in the LINQ query.
The following also returns the correct result:
entities.JOBRUNS.ToList().Where(x => x.JOB_NAME == job && x.JOB_INFO == "SUCCESS").Count();
The difference here is the call to ToList on the table before applying the where clause. This means two things:
The data is in the database and it is correct.
The problem seems to be the query including the where clause when executed by the EF Provider.
What really stuns me is, that this is a live system and the problem occurred without any changes to the database or the program. One call to that method returned the correct result and the next call five minutes later returned the wrong result. And since then, it only returns the wrong results.
Any hints, suggestions, ideas etc. are welcome, never mind, how far-fetched they seem! Please post them as answers, so I can vote on them, just for the fact for reading my lengthy question and bothering thinking about that strange problem... ;-)
First of all remove ObjectContext caching. Object context internally uses UnitOfWork and IdentityMap patterns. This can have big impact on queries.

Categories

Resources