I have a piece code written in an ASP Core 7 API, with EF Core 7, using MSSQL for the database. The below code is where the repository loads some data for the dashboard.
public async Task<ResponseResult<DashboardPendingBillsDto>> PendingBillDetails(CancellationToken token)
{
var venderInvoicelQuery = _context.Set<VendorInvoice>().AsQueryable();
var pendingBillsQuery = venderInvoicelQuery.Where(a => !a.IsBilled);
var today = DateTimeOffset.UtcNow;
var fourthDay = today.AddDays(-3);
var seventhDay = today.AddDays(-7);
var pendingBillsData = await pendingBillsQuery.Select(s => new
{
BetweenOneToFiveDays = pendingBillsQuery
.Where(a => fourthDay.Date < a.CreatedOn.Date && a.CreatedOn.Date <= today.Date).Count(),
BetweenSixToTenDays = pendingBillsQuery
.Where(a => seventhDay.Date < a.CreatedOn.Date && a.CreatedOn.Date <= fourthDay.Date).Count(),
AboveTenDays = pendingBillsQuery
.Where(a => a.CreatedOn.Date <= seventhDay.Date).Count()
}).FirstOrDefaultAsync(token);
var dashboardPendingBillsDto = new DashboardPendingBillsDto
{
BetweenZeroToThreeDays = pendingBillsData?.BetweenOneToFiveDays ?? 0,
BetweenFourToSevenDays = pendingBillsData?.BetweenSixToTenDays ?? 0,
AboveSevenDays = pendingBillsData?.AboveTenDays ?? 0,
};
return new ResponseResult<DashboardPendingBillsDto>(dashboardPendingBillsDto);
}
The above code returns the desired data as well as shown below
The problem is, if the HTTP request was cancelled, then the TaskCanceledException exception is thrown and it gives an error An error occurred using the connection to database 'test-db-12-jan-2023' on server 'DESKTOP-xxx. But i already passed the cancelation token into the FirstOrDefaultAsync(token) as such. it happens only if the http request was cancelled. Any help please.
update 29-jan-2023
I have a middle ware to handle exceptions globally and it is the first middle ware of the pipeline
And here is how the middleware handles the exceptions. i have only 2 exceptions here, TaskCanceledException and Exception
private Task ConvertException(HttpContext context, Exception exception)
{
var activityId = Activity.Current?.Id ?? "N/A";
_telemetry.TrackException(exception);
ErrorResponse errorResponse = new() { TraceId = activityId };
int httpStatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = "application/json";
var result = string.Empty;
switch (exception)
{
case TaskCanceledException:
//if client closes the connection
httpStatusCode = StatusCodes.Status200OK;
result = JsonConvert.SerializeObject(new ResponseResult<string>("Client closed the connecion"));
break;
case Exception:
httpStatusCode = StatusCodes.Status500InternalServerError;
errorResponse.Errors.Add(new KeyValuePair<string, IEnumerable<string>>(nameof(HttpStatusCode.InternalServerError), new[] { "Something went wrong, please try again" }));
result = JsonConvert.SerializeObject(errorResponse);
LogError(exception, activityId);
break;
}
context.Response.StatusCode = httpStatusCode;
return context.Response.WriteAsync(result);
}
Below is the exception message i get from the middleware
A task was cancelled
Below is the the stack trace from the exception which i can get from the middleware
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.<OpenInternalAsync>d__70.MoveNext()
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.<OpenInternalAsync>d__70.MoveNext()
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.<OpenAsync>d__66.MoveNext()
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__19.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<InitializeReaderAsync>d__21.MoveNext()
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.<ExecuteAsync>d__7`2.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<MoveNextAsync>d__20.MoveNext()
at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__15`1.MoveNext()
at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleOrDefaultAsync>d__15`1.MoveNext()
at VPMS.Persistence.Repositories.Dashboard.DashboardRepository.<PendingBillDetails>d__3.MoveNext() in C:\Projects\Legacy HealthCare - Vendor Payment Management System\VPMS\VPMS.Persistence\Repositories\DashboardRepository\DashboardRepository.cs:line 162
at VPMS.Application.DashBoard.Services.DashboardService.<GetPendingBillsAsync>d__3.MoveNext() in C:\Projects\Legacy HealthCare - Vendor Payment Management System\VPMS\VPMS.Application\DashBoard\Services\DashboardService.cs:line 25
at VPMS.Api.Controllers.Dashboard.DashboardController.<GetPendingBills>d__2.MoveNext() in C:\Projects\Legacy HealthCare - Vendor Payment Management System\VPMS\VPMS.Api\Controllers\DashBoardController.cs:line 30
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.<Execute>d__0.MoveNext()
at System.Threading.Tasks.ValueTask`1.get_Result()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeNextActionFilterAsync>g__Awaited|10_0>d.MoveNext()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeFilterPipelineAsync>g__Awaited|20_0>d.MoveNext()
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeAsync>g__Logged|17_1>d.MoveNext()
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeAsync>g__Logged|17_1>d.MoveNext()
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<<Invoke>g__AwaitRequestTask|6_0>d.MoveNext()
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.<Invoke>d__9.MoveNext()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.<Invoke>d__6.MoveNext()
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.<Invoke>d__5.MoveNext()
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.<Invoke>d__4.MoveNext()
at VPMS.Api.Middleware.ExceptionHandlerMiddleware.<Invoke>d__3.MoveNext() in C:\Projects\Legacy HealthCare - Vendor Payment Management System\VPMS\VPMS.Api\Middleware\ExceptionHandlerMiddleware.cs:line 25
But i have serilogged configured and it gives an exception which does not hit the middle ware which is the real problem in here which is
An error occurred using the connection to database 'test-db-12-jan-2023' on server 'DESKTOP-xxx
The above error message is logged to the serilog but it never reaches the middleware and this is the exception i want to avoid. this is the problem here.
Related
Ooookay, so this has been a thorn in my side for a good week now.
First off, this is for Framework 4.8 (beyond my control).
I make a list of tasks, using Task.Run and a lambda to call an async function that calls a 3rd party API and returns a: Task<List<Dictionary<string, object>>>
Number of tasks is 75.
The API has pagination requirements, so the calling function takes start and end parameters, based on the iterator of the loop that builds the task list.
So far all straight forward.
Also straight forward is that after Task.WaitAll I get the wrong results, because closures close over variables, not over values. The iterator is at max long before the tasks have spun off, and thus the vast majority end up with the wrong pagination.
**** However, they do all run to completion.****
Now, whenever I try to solve the closure issue, I end up with a consistent result where only some of the tasks run to completion and the rest all fail. I have tried:
Assigning the iterator to a new variable in the lambda
Using Task.Factory.StartNew and passing the iterator in as a state object
Variations of the above, but with the start and end calculations moved to the calling function
In all cases the result remains the same: anywhere from a handful to a third of the tasks complete and the rest all fail, usually the former.
The AggregateException collection offers little to no insight as far as I can understand.
The general exception is the usual "One or more errors have occurred" and the inner exceptions hints at a movenext failure(?). Stack traces are minimal one-liners.
What could be going on here?
Thanks for the help.
-Willem
The calling function
public async Task<List<Dictionary<string, object>>> ExecuteAsyncJSON(
string requestSource = "fields", string searchCount = "", Object iteration = null)
{
string url = requestSource;
// Add possible parameters.
if (searchCount != "" && iteration != null)
{
// divide count by 500 + remainder for pagination
int quotient = (Int32.Parse(searchCount) / 500);
int remainder = Int32.Parse(searchCount) % 500;
int i = Convert.ToInt32(iteration);
int add = i == quotient ? remainder : 500;
int start = i * 500;
int end = (i * 500) + add;
if (url.Contains("?"))
{
url = url + "&start=" + start.ToString() + "&end=" + end.ToString();
}
else
{
url = url + "?start=" + start.ToString() + "&end=" + end.ToString();
}
}
try
{
// make the call and get the response
using (HttpResponseMessage response = await someclient.GetAsync(url))
// this calls the API through an HttpClient
{
if (response.IsSuccessStatusCode)
{
var serializer = new JavaScriptSerializer();
var responseBodyAsText = await response.Content.ReadAsStringAsync();
var returnData = new List<Dictionary<string, object>>();
returnData = serializer
.Deserialize<List<Dictionary<string, object>>>(responseBodyAsText);
return returnData;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
catch
{
throw new Exception();
}
}
The main stuff that tries to get this all done
var requestSource = "searches/" + searchId + "/members";
var searchCount = 37301;
int quotient = (Int32.Parse(searchCount) / 500);
var tasks = new List<Task<List<Dictionary<string, object>>>>();
// Put the task list together.
// Using the StartNew with state object to pass the iterator in.
for (var i = 0; i <= quotient; i++)
{
var task = Task.Factory.StartNew((iteration) =>
{
var returnData = ExecuteAsyncJSON(requestSource, searchCount, iteration);
return returnData;
}, i)
.Unwrap();
tasks.Add(task);
}
try
{
Task.WaitAll(tasks.ToArray());
}
catch (AggregateException ae)
{
//throw ae;
}
List<Dictionary<string, object>> completedResults
= new List<Dictionary<string, object>>();
List<Task<List<Dictionary<string, object>>>> faultedResults
= new List<Task<List<Dictionary<string, object>>>>();
foreach (Task<List<Dictionary<string, object>>> listTask in tasks)
{
if (listTask.Status == TaskStatus.RanToCompletion)
completedResults.AddRange(listTask.Result);
if (listTask.Status == TaskStatus.Faulted)
faultedResults.Add(listTask);
}
Debug Data
The WaitAll AggregateException is:
One or more errors occurred
Exception of type 'System.Exception' was thrown.
at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
at Smu.Bbec.Emma.API.EmmaAPI.<GetMembersMatchingSearch>d__22.MoveNext() in [reference to Task.WaitAll(tasks.ToArray());]
Out of 76 calls, 4 completed just fine and then 72 fail with the exact same inner exception:
System.Exception: Exception of type 'System.Exception' was thrown.
at Smu.Bbec.Emma.API.EmmaAPI.<ExecuteAsyncJSON>d__8.MoveNext() in [reference to code that makes the actual call to the API]
I set some breakpoints and this is the result:
A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Smu.Bbec.Emma.API.EmmaAPI.<ExecuteAsyncJSON>d__8.MoveNext() in f:\\TestApps\\Smu.Bbec.Emma\\Smu.Bbec.Emma.API\\EmmaAPI.cs:line 100"
No inner exception.
And this variety;
An error occurred while sending the request.
Inner exception: The request was aborted: The request was canceled (System.Exception {System.Net.WebException})
This is the full debug data I have on the cancelation (sensitive data removed):
$exception {"A task was canceled."} System.Threading.Tasks.TaskCanceledException
this {**********.EmmaAPI} **********.EmmaAPI
_accountId "********" string
_authorization "MTg4NzblahblahblahblahmM6MDNmYzU1blahblahblahblahjM=" string
_baseUri {https://api.e2ma.net/********/} System.Uri
_baseUrl "https://api.e2ma.net/********/" string
_publicKey "********" string
_secretKey "********" string
emmaClient {System.Net.Http.HttpClient} System.Net.Http.HttpClient
BaseAddress {https://api.e2ma.net/********/} System.Uri
DefaultRequestHeaders {Accept: application/json Authorization: Basic MblahblahblahblahTFjYmblahblahblahblahM4M2IyMjM=} System.Net.Http.Headers.HttpRequestHeaders
MaxResponseContentBufferSize 2147483647 long
Timeout {00:01:40} System.TimeSpan
baseAddress {https://api.e2ma.net/********/} System.Uri
defaultRequestHeaders {Accept: application/json Authorization: Basic MTgblahblahblahblah4Y2blahblahblahblahNmYzblahblahblahblahjM=} System.Net.Http.Headers.HttpRequestHeaders
disposeHandler true bool
disposed (System.Net.Http.HttpMessageInvoker) false bool
disposed false bool
handler {System.Net.Http.HttpClientHandler} System.Net.Http.HttpMessageHandler {System.Net.Http.HttpClientHandler}
maxResponseContentBufferSize 2147483647 long
operationStarted true bool
pendingRequestsCts {System.Threading.CancellationTokenSource} System.Threading.CancellationTokenSource
timeout {00:01:40} System.TimeSpan
Static members
requestSource "searches/17297143/members" string
searchCount "37810" string
iteration 67 object {int}
url "searches/17297143/members?start=33500&end=34000" string
I found the answer to my conundrum.
It turned out to be the same issue as posted here:
c# a task was canceled at the await call
The solution for me was to add .Timeout = TimeSpan.FromMinutes(30) to my HttpClient.
Thanks to Theodor Zoulias, Stephen Cleary, and everyone else who helped me out here.
Cheers!
I'm using microsoft-bot-framework and the pro active example using the NotifyController. https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages.
So far all good, I'm able to send a pro active message to a user:
The issue: turnContext throws a nullref error, but only after ~ 1 hour. If the user chats again first to the bot, there is no issue and the turnContext is known.
The nullref error is thrown at ((BotAdapter)_adapter).ContinueConversationAsync...
namespace ProactiveBot.Controllers
{
[Route("api/notify")]
[ApiController]
public class NotifyController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly string _appId;
public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
{
_adapter = adapter;
_appId = configuration["MicrosoftAppId"] ?? string.Empty;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] ChatbotResponse response)
{
// Set basic reply
var reply = MessageFactory.Text(response.Text);
if(response?.Buttons?.Count > 0) // a reply with buttons
{
reply = SendSuggestedActions(response.Text,default,response?.Buttons);
}
// Target a specific user
if (response.Recipient_id != string.Empty)
{
var recipient = UserCollection.UserSession.FirstOrDefault(x => x.Value.ID == response.Recipient_id).Value;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId,recipient.ConversationReference,(ITurnContext turnContext,CancellationToken cancellationToken) => turnContext.SendActivityAsync(reply),default);
Error log
2021-03-31 06:22:07.559 +00:00 [Error] Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer: Connection ID "(xxxxx)", Request ID "(xxxxxx)": An unhandled exception was thrown by the application.System.NullReferenceException: Object reference not set to an instance of an object.at ProactiveBot.Controllers.NotifyController.Post(ChatbotResponse response) in C:\xxxxx\xxxxxxxx\workspace\microsoft-bot-framework\CoreBot\Controllers\NotifyController.cs:line 51at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()
------------- Update: alternative approach -------------
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId,recipient.ConversationReference,async (context,token) =>
await BotCallback(activity,context,token),default(CancellationToken));
were BotCallback is
private async Task BotCallback(Activity activity, ITurnContext turnContext, CancellationToken token)
{
var response = await turnContext.SendActivityAsync(activity);
}
throws the same error. Puzzled why turnContext works in the first hour but is null after more then an hour.
Okay, so as promised. Here's how I got proactive messaging to users working...
What you need:
The user's ID (userId in the below example) in Teams. You can get this from a prior interaction or just from querying the users list (GetConversationPagedMembersAsync).
The Team's tenant ID (tenantId in the below example). When a user initially installs our bot, I collect this information and store it in our database. Note that it can apparently change over time, so I do check it again periodically.
The code:
// we only need the user's ID, which we'll get from the DB
var user = new ChannelAccount(userId);
var conversationParams = new ConversationParameters
{
IsGroup = false,
Members = new List<ChannelAccount>
{
user
},
TenantId = tenantId
};
try
{
var conversation = await _client.Conversations.CreateConversationAsync(conversationParams);
message.Conversation = new ConversationAccount(id: conversation.Id);
var response = await _client.Conversations.SendToConversationAsync(message);
return new MessageReference
{
ConversationId = conversation.Id,
MessageId = response.Id
};
}
catch (Exception ex)
{
// log, or whatever
}
Note that it seemed to be important to create the conversation first, and only then call SendToConversationAsync, unlike messaging channels where it seems that you could supply the message activity as part of the ConversationParameters.
This is how I do all proactive messaging. I don't use ITurnContext at all, except where I'm responding to a user-initiated message, e.g. when they first install the bot to their channel/personal scope.
I am looking into an issue with some legacy code which is relaying a Http Request to another URL. It is an API Controller that reads the request and forwards it to another address asynchronously.
Very rarely, it throws a "Cannot access a disposed object" exception - full stack trace shown later in this question. It seems to be on the line where the HttpClient is calling the SendAsync method. I think it might be the ResponseHeadersRead option - I suspect it is happening when a large packet is being sent and it gets closed because it has just read the header and quit. Just thought I'd sanity check this with you all, for your thoughts. I'll change the option to be ResponseContentsRead option and see how that goes (but it can take a long time for the error to surface).
Here is the code:
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(Request);
await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
}
private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
var forwardToUrl = new Uri(ConfigurationManager.AppSettings["ForwardFeedURL"]);
var relayRequest = new HttpRequestMessage(incomingRequest.Method, forwardToUrl);
if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
{
relayRequest.Content = incomingRequest.Content;
}
//Copies contents
relayRequest.Content = incomingRequest.Content;
return relayRequest;
}
And here is the exception:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Web.Http.WebHost.HttpControllerHandler+LazyStreamContent'.
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.CopyToAsync(Stream stream, TransportContext context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at CHO.Web.Services.ETrains.Controllers.ETrainsApiController.<CopyandForwardFeedAsyn>d__18.MoveNext() in \Controllers\MyAPIController.cs:line 289
Note, line 289 is the "await client.SendAsync" line of code
Odds are there is an error code being set by the server.
Follow your code with a response.EnsureSuccessStatusCode(); that is wrapped in a Try Catch block:
try
{
var request = BuildRelayHttpRequest(Request);
await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
// Handle success
}
catch (HttpRequestException)
{
// Handle failure
}
I have a web service that should pull about 80 records from SQL database. However recently it started giving me out of memory error.
When I debug it - it shows me the web service on the list, once I run it - get the error below.
webmethod:
[WebMethod(Description = "Getting Weekly Events by Facility, future events from sunday", CacheDuration = 600)]
public List<EventViewModel> GetWeeklyEvents(string facilityNumber, DateTime StDate)
{
var db = new DS_AIMDataContext();
var eventList = from evt in db.GetPublishedEventsFromSundayByFacility(facilityNumber, StDate)
select new EventViewModel
{
EventName = evt.ActName,
EventNameSpanish = evt.ActNameSp,
EventDescription = evt.ActDescription,
EventDescrSpanish = evt.ActDescrSp,
StDate = evt.EventStart.Value,
EndDate = evt.EventEnd.Value,
EventCategory = evt.CategoryName,
EventCatID = evt.ActCategID.Value,
EventType = evt.ActType,
EventLocation = evt.LocName,
EventLeader = evt.LeaderName,
EventCategorySp = evt.CategoryNameSp,
EventTypeSp = evt.ActTypeSp,
EventRecurrenceRule = evt.RecurrenceRule,
EventPhoto1 = evt.Photo1 == null ? null : evt.Photo1.ToArray(),
EventPhoto2 = evt.Photo2 == null ? null : evt.Photo2.ToArray(),
EventPhoto3 = evt.Photo3 == null ? null : evt.Photo3.ToArray()
};
return eventList.ToList();
}
The function itself:
public IEnumerable<tblEventsWithRecurr> GetPublishedEventsFromSundayByFacility(string facN, DateTime StDate)
{
var eventList = from evt in this.tblEventsWithRecurrs
orderby Convert.ToDateTime(evt.EventStart.ToString()).Day, Convert.ToDateTime(evt.EventStart.ToString()).Hour, Convert.ToDateTime(evt.EventStart.ToString()).Minute
where (evt.FacN == facN && evt.EventStatus == "Final" && evt.EventStart >= StDate && evt.EventStart <= StDate.AddDays(31))
select evt;
return eventList;
}
Error that I'm getting is below:
Server Error in '/' Application.
Exception of type 'System.OutOfMemoryException' was thrown.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.]
System.IO.MemoryStream.set_Capacity(Int32 value) +93
System.IO.MemoryStream.EnsureCapacity(Int32 value) +90
System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) +326
Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.ArteryFilter.Write(Byte[] buffer, Int32 offset, Int32 count) +61
System.Web.HttpWriter.FilterIntegrated(Boolean finalFiltering, IIS7WorkerRequest wr) +9641608
System.Web.HttpResponse.FilterOutput() +104
System.Web.CallFilterExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +58
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69
Couple of things:
First, how big are your Photo files - you have 3 photo's you're sending back. It would not be difficult to imagine a dozen very large photos on all of your results to max out whatever memory you have going on.
Secondly:
You may want to consider disposing of your datacontext.
Currently you have:
var db = new DS_AIMDataContext();
And this is never explicitly disposed, meaning the GC will eventually get to it, where you could instruct it to be disposed of the moment you are finished with it.
So try that line to:
using (DS_AIMDataContext db = new DS_AIMDataContext()){
// all of your eventList projection
// and your return statement
}
This will call Dispose as soon as your method completes and will hopefully free up memory.
Because of the fact that we are required to connect to an LDAP server using LDAPS we must use LdapConnection instead of DirectoryEntry.
Here is the source code:
SearchResponse response;
using (LdapConnection con = new LdapConnection(new LdapDirectoryIdentifier(Host, Port)))
{
if (IsSSL)
{
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate =
(connection, certificate)
=> true;
}
con.Credential = new NetworkCredential(_username, _password);
con.AuthType = AuthType.Basic;
con.Bind();
if (logMessage != null)
logMessage("Connected to LDAP");
string sFilter = String.Format(
"(&(objectcategory=person)(objectclass=user){0}(!(userAccountControl:1.2.840.113556.1.4.803:=2)))",
filter
);
SearchRequest request = new SearchRequest("OU=Corp,DC=mydc,DC=com", sFilter, SearchScope.Subtree);
request.Attributes.Add(Resources.objectguid);
request.Attributes.Add(Resources.givenname);
request.Attributes.Add(Resources.sn);
request.Attributes.Add(Resources.initials);
request.Attributes.Add(Resources.samaccountname);
request.Attributes.Add(Resources.userprincipalname);
request.Attributes.Add(Resources.mail);
request.Attributes.Add(Resources.objectsid);
request.Attributes.Add(Resources.department);
request.Attributes.Add(Resources.company);
request.SizeLimit = 10;
response = (SearchResponse) con.SendRequest(request);
}
Upon execution of the source code (we have verified credentials, host, port, etc - using an external 3rd party software) we get the following exception:
The size limit was exceeded
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.DirectoryServices.Protocols.DirectoryOperationException: The size limit was exceeded
Source Error:
response = (SearchResponse) con.SendRequest(request);
[DirectoryOperationException: The size limit was exceeded]
System.DirectoryServices.Protocols.LdapConnection.ConstructResponse(Int32
messageId, LdapOperation operation, ResultAll resultType, TimeSpan
requestTimeOut, Boolean exceptionOnTimeOut) +2385
System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest
request, TimeSpan requestTimeout) +499
System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest
request) +50
UserSearchProvider.ADUserSearchProvider.QueryStore(UserSearchCriteriaCollection
criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual
Studio
2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\ADUserSearchProvider.cs:298
UserSearchProvider.UserSearchProvider.QueryAndSort(UserSearchCriteriaCollection
criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual
Studio
2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\UserSearchProvider.cs:77
UserSearchProvider.UserSearchProvider.Search(UserSearchCriteriaCollection
criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual
Studio
2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\UserSearchProvider.cs:33
UserSearchProvider.UserSearchService.Search(UserSearchCriteriaCollection
criterias, Action1 logMessage) in c:\Users\stemarie\Documents\Visual
Studio
2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProvider\UserSearchService.cs:44
UserSearchProviderTest._Default.Page_Load(Object sender, EventArgs e) in c:\Users\stemarie\Documents\Visual Studio
2012\Projects\Idealink.Modules\UserSearchProvider\UserSearchProviderTest\Default.aspx.cs:28
The part that confuses me is that we did specify the maximum size limit, we don't want more than 100 entries - we want to limit it. But yet the library consistently throws the error even if we specify a SizeLimit of 1.
Does anyone have any insights/suggestions regarding this issue? We are very close to getting this working and just need to resolve this last problem.
You should use cookies in a function similar to this.
The function returns a collection of SearchResponse objects, which the caller should loop though.
private List<SearchResponse> SearchDirectory(string distinguishedName, string searchFilter, System.DirectoryServices.Protocols.SearchScope searchScope, params string[] attributeList)
{
List<SearchResponse> result = new List<SearchResponse>();
SearchResponse response = null;
int maxResultsToRequest = 100;
try
{
PageResultRequestControl pageRequestControl = new PageResultRequestControl(maxResultsToRequest);
// used to retrieve the cookie to send for the subsequent request
PageResultResponseControl pageResponseControl;
SearchRequest searchRequest = new SearchRequest(distinguishedName, searchFilter, searchScope, attributeList);
searchRequest.Controls.Add(pageRequestControl);
while (true)
{
response = (SearchResponse)connection.SendRequest(searchRequest);
result.Add(response);
pageResponseControl = (PageResultResponseControl)response.Controls[0];
if (pageResponseControl.Cookie.Length == 0)
break;
pageRequestControl.Cookie = pageResponseControl.Cookie;
}
}
catch (Exception e)
{
// do something with the error
}
return result;
}
As it turns out, this works:
try
{
response = (SearchResponse)con.SendRequest(request);
return response.Entries.Cast<SearchResultEntry>()
.Select(entry => entry.Attributes)
.Select(x => GetADUserSearchItemFromADProperties(x, logMessage))
.Where(user => user.HasName)
.Cast<IUserSearchItem>();
}
catch (DirectoryOperationException ex)
{
response = (SearchResponse) ex.Response;
return response.Entries.Cast<SearchResultEntry>()
.Select(entry => entry.Attributes)
.Select(x => GetADUserSearchItemFromADProperties(x, logMessage))
.Where(user => user.HasName)
.Cast<IUserSearchItem>();
}
The MSDN documentation states that you get a DirectoryResponse class as the DirectoryOperationException.Response property. You can however cast this property to a SearchResponse type and then use the SearchResponse.Entries property to get the entries that you have received prior to hitting the specified SizeLimit.
I have tried this and I get the expected results, I'm just a bit upset that I have to work with an exception in order to perform the operation.