Task`1 cannot be serialized for Web Application referencing BCL.Async - c#

We recently deployed a newly developed pre-compiled service to our test domain and received the following error:
Type 'System.Threading.Tasks.Task`1[Domain.Infrastructure.Contracts.Configuration.DomainServices]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
The server is Windows 2008R2 running .NET 4.0.
There has been a few Stack Overflow questions about this, but most seem to be referring to the CTP release of Async. Apparently you MUST have .NET 4.5 installed on the server in order to use this code.
Has this situation changed at all with the release of the BCL.Async NuGet package?
I was under the impression that code that was compiled with an Async compiler and includes the BCL libraries from NuGet had everything they needed to run in a .NET 4 environment.
Is it still the case that we have to upgrade the .NET runtime on the server to 4.5?
Edit: Stack Trace Provided:
[InvalidDataContractException: Type 'System.Threading.Tasks.Task`1[Domain.Infrastructure.Contracts.Configuration.DomainServices]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.]
System.Runtime.Serialization.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) +1184850
System.Runtime.Serialization.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type) +787
System.Runtime.Serialization.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type) +117
System.Runtime.Serialization.XsdDataContractExporter.GetSchemaTypeName(Type type) +85
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.CreatePartInfo(MessagePartDescription part, OperationFormatStyle style, DataContractSerializerOperationBehavior serializerFactory) +48
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.CreateMessageInfo(DataContractFormatAttribute dataContractFormatAttribute, MessageDescription messageDescription, DataContractSerializerOperationBehavior serializerFactory) +708
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter..ctor(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory) +570
System.ServiceModel.Description.DataContractSerializerOperationBehavior.GetFormatter(OperationDescription operation, Boolean& formatRequest, Boolean& formatReply, Boolean isProxy) +308
System.ServiceModel.Description.DataContractSerializerOperationBehavior.System.ServiceModel.Description.IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) +69
System.ServiceModel.Description.DispatcherBuilder.BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch) +120
System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost) +4250
System.ServiceModel.ServiceHostBase.InitializeRuntime() +82
System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +64
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +789
System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +255
System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +1172
[ServiceActivationException: The service '/Services/Binary/Endpoint.svc' cannot be activated due to an exception during compilation. The exception message is: Type 'System.Threading.Tasks.Task`1[Domain.Infrastructure.Contracts.Configuration.DomainServices]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types..]
System.Runtime.AsyncResult.End(IAsyncResult result) +901504
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +178638
System.Web.AsyncEventExecutionStep.OnAsyncEventCompletion(IAsyncResult ar) +107

Okay, I'm going to take a good guess here that you're trying to expose an asynchronous WCF operation that returns a Task<Domain.Infrastructure.Contracts.Configuration.DomainServices>. While Microsoft.Bcl.Async will allow you to compile code that uses tasks, it won't provide the .NET Framework 4.5 changes to WCF that allow you to use tasks in services.
That being said, you can still use the asynchronous programming model to expose the asynchronous method to WCF, while still writing the code using the TPL. To do so, you'll have to wrap the method with APM begin/end methods. Something like this:
[ServiceContractAttribute]
public interface ISampleService
{
[OperationContractAttribute]
string SampleMethod();
[OperationContractAttribute(AsyncPattern = true)]
IAsyncResult BeginSampleMethod(AsyncCallback callback, object asyncState);
string EndSampleMethod(IAsyncResult result);
}
public class SampleService : ISampleService
{
// the async method needs to be private so that WCF doesn't try to
// understand its return type of Task<string>
private async Task<string> SampleMethodAsync()
{
// perform your async operation here
}
public string SampleMethod()
{
return this.SampleMethodAsync().Result;
}
public IAsyncResult BeginSampleMethod(AsyncCallback callback, object asyncState)
{
var task = this.SampleMethodAsync();
if (callback != null)
{
task.ContinueWith(_ => callback(task));
}
return task;
}
public string EndSampleMethod(IAsyncResult result)
{
return ((Task<string>)result).Result;
}
}

Related

using DbContext in a manually created scope in a singleton service results in ObjectDisposedException

Note: In the hour I spent writing this post and in the process having a second and third look at everything, I found the solution to my problem. I decided to post this anyway, as I did not find the solution online and it might help others.
If you have any comments about my architecture, please do share them! My guts tell me I should not have stumbled across this problem in the first place, but I can't point my finger to a specific detail.
In my .NET 6 application I have a service which needs to access the database, thus use a DbContext. The service is registered as singleton, but the DbContext is not, as it should be. Quite a number of similar questions out there are answered with inject IServiceScopeFactory and create (and dispose) your scope for each operation. example 1, example 2, example from Microsoft.
I try to do exactly that, but it throws ObjectDisposedExceptions, so it obviously does not work in my case. The question is: why?
The service(s) I'm talking about are data access services for the rest of the application, the gateway to the database. They are registered as singletons, because they fire events consumed by background services for certain actions.
I moved the context and scope handling into an abstract class, that looks like this (stripped down):
(I obviously renamed all classes, I'm not working on a cat related application, if something does not add up, I might have forgotten to remane something 😁)
public abstract class RepositoryBase
{
private readonly IServiceScopeFactory serviceScopeFactory;
protected RepositoryBase(IServiceScopeFactory serviceScopeFactory)
{
this.serviceScopeFactory = serviceScopeFactory;
}
protected void ExecuteInContext(Action<IAnimalContext> function)
{
using var scope = serviceScopeFactory.CreateScope();
function(scope.ServiceProvider.GetRequiredService<IAnimalContext>());
}
protected T ExecuteInContext<T>(Func<IAnimalContext, T> function)
{
using var scope = serviceScopeFactory.CreateScope();
return function(scope.ServiceProvider.GetRequiredService<IAnimalContext>());
}
}
This is then used like so:
internal class CatRepository : RepositoryBase, ICatRepository
{
public CatRepository(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
{ }
public event Action<Guid>? CatRenamed;
public IEnumerable<Cat> AllCats => ExecuteInContext(context => context.Cats);
public void RenameCat(Guid id, string name) => ExecuteInContext(context => {
var cat = context.Cats.FindOrThrow(id);
cat.Name = name;
context.SaveChanges();
CatRenamed?.Invoke(cat.id);
});
If I'm removing the using statements from the ExecuteInContext functions, it behaves as expected (creating a new scope every time a repository action is called and never disposing the scopes).
Debugging with breakpoints (as well as log messages) in the ExecuteInContext functions indicate that the actions are executed completely before the scope is disposed.
The stack trace of the exception has no functions or classes written by me in it. When calling CatRepository.AllCats it looks something like this:
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying
to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injectio
n, you should let the dependency injection container take care of disposing context instances.
Object name: 'LogisticsStateManagementContext'.
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.DbContext.get_ChangeTracker()
at Microsoft.EntityFrameworkCore.Query.CompiledQueryCacheKeyGenerator.GenerateCacheKeyCore(Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.RelationalCompiledQueryCacheKeyGenerator.GenerateCacheKeyCore(Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerCompiledQueryCacheKeyGenerator.GenerateCacheKey(Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.IncludableQueryable`2.GetEnumerator()
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at System.Text.Json.Serialization.Converters.IEnumerableDefaultConverter`2.OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryWrite(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.WriteCoreAsObject(Utf8JsonWriter writer, Object value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteCore[TValue](JsonConverter jsonConverter, Utf8JsonWriter writer, TValue& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
at System.Text.Json.JsonSerializer.WriteStreamAsync[TValue](Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object
state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isComplet
ed)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
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.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authori
zeResult)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
The stack trace is where I found my solution, see answer below.
The stack trace indicates, that the exception occurs during JSON serialization. It is not visible from my code examples, but the IEnumerable returned is actually an IQueryable. This means the database is only called once it is enumerated, which in this particular case only happens when I try to serialize the data to send it elsewhere. At that point the context is already disposed, but not GC'd, since the IQueryable has a reference to it.
I knew of this behaviour of IQueryable, just did not see the problem here, as the return value is IEnumerable<Cat>. I basically masked the reference to a context for myself and let it escape the scope in which the context exists.
So the general answer to my question is: Make sure no references to the context leave the scope.
The concrete answer in my example above would be to change this:
public IEnumerable<Cat> AllCats => ExecuteInContext(context => context.Cats);
to this
public IEnumerable<Cat> AllCats => ExecuteInContext(context => context.Cats.ToArray());
so that the enumeration is happening inside the scope where the context exists.

Unable to deserialize PSObject in response data contract. Running out of leads

Background:
The company I work at has a product exposed to the network via WCF, specifically, through a service contract containing a single method that handles all messages. Internally, the product parses the SOAP header and body, loads a service plug-in, and routes the request to that plug-in.
I have written one of these plug-ins to host the PowerShell runtime, and provided a custom host that hooks into some of the internal plumbing of this product. A remote client could send an arbitrary script to the server to execute, including taking advantage of functionality of the product itself.
To be clear: I am well aware of the security implications. This plug-in is only for diagnostic and prototyping purposes, not for production use or deployment.
Problem:
In the data contract sent back as a response, I am passing an array of PSObject instances:
[DataContractAttribute]
public partial class RunScriptResponse
{
[DataMember]
public PSObject[] Output { get; set; }
}
According to this article, PSObject should be supported; it implements ISerializable and boasts SerializableAttribute.
The response data contract is getting serialized just peachy by the server, and is being received without issue by the client--as verified by WCF extensibility points. However, when the service client tries to deserialize the objects, I hit this [1]:
Member 'CliXml' was not found.
Yet, looking at the response XML, it's pretty darn clear, that member is there:
<RunScriptResponse xmlns="...">
<RunScriptResult xmlns:d1p1="..." xmlns:e="..." xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<e:Output xmlns:d1p1="http://schemas.datacontract.org/2004/07/System.Management.Automation">
<d1p1:PSObject>
<CliXml i:type="d3p1:string" xmlns:d3p1="http://www.w3.org/2001/XMLSchema">
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
It suggests that something is happening during deserialization where the PSObject isn't able to find the <CliXml> element, but I can't figure out why.
Also, the particular client that is trying to access this server is itself a PowerShell module, dynamically generated from proxy assemblies via reflection. The intent is that you can pass a script to the server, get some PSObject instances back, and do further processing on the client.
Given its auto-generated nature, it's not possible to switch to other serialization scenarios, such as serializing the PSObject instances on the server, passing an array of strings, and then deserializing them on the client. That would require unique-to-one-method handling, which would significantly complicate the module generation process.
Does anyone have any suggestions on how to further troubleshoot this, or other strategies?
Thank you!
[1] Stack trace for exception:
at System.Runtime.Serialization.SerializationInfo.GetElement(String name, Type& foundType)
at System.Runtime.Serialization.SerializationInfo.GetValue(String name, Type type)
at System.Management.Automation.PSObject..ctor(SerializationInfo info, StreamingContext context)
at ReadPSObjectFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 id, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
at ReadArrayOfPSObjectFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString , XmlDictionaryString , CollectionDataContract )
at System.Runtime.Serialization.CollectionDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 id, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
at ReadRunScriptResponseFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.PartInfo.ReadObject(XmlDictionaryReader reader, XmlObjectSerializer serializer)
at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
20180129: The code that I'm using on the client side is plain vanilla WCF. I exported the WSDL and XSDs from the service interface assembly via svcutil.exe, ran those back through svcutil.exe to generate a C# proxy. From there, everything is just calling the proxy as-is:
// Utility code that wraps the programmatic generation of a proxy.
// Manually instantiates `WSHttpBinding` and `EndpointAddress`, then
// constructs an instance of the proxy client using those two objects.
//
var client = GenerateProxyClient<RunScriptRequest,
IScriptingService,
ScriptingServiceClient,
RunScriptResponse>();
var response = client.RunScript(new RunScriptRequest
{
Script = #"Get-ChildItem C:\"
});

WCF ObjectDisposedException Safe Handle has been released / ASP.NET MVC3 Client / Threading

Assume that:
An ASP.NET MVC3 Page (using visual studio debug server) calls a WCF Service (self hosted, net.tcp binding).
Service uses ThreadPool to do some work, but returns immediately a value to the client
Client gets the value and redirects to another page.
A few seconds later the Server crashes with an "ObjectDisposedException / Safe handle has been released)
First I thought that the proxy would be disposed after the page redirect and the wcf service would get disposed too, but even when I'm not using the ThreadPool (by calling the method within the wcf service thread) I get the exception too. I don't get it.
I'm not really into web programming. What am I doing wrong?
Client
Global.asax (registering channel factory)
protected void Application_Start()
{
builder.Register(c => new ChannelFactory<IWcfBakeryService >("")).SingleInstance();
builder.Register(c => c.Resolve<ChannelFactory<IWcfBakeryService>>()
.CreateChannel())
.UseWcfSafeRelease();
}
Repository (channel injected by Autofac)
public BakeryRepository(IWcfBakeryService orderService)
{
}
public ReciepeResponse RequestReciepe(CakeDto webOrder)
{
[...]
return service.RequestReciepe(request);
}
Server:
Contract
[ProtoBehavior, OperationContract(IsTerminating=false)]
ReciepeResponse RequestReciepe(ReciepeRequest request);
Service-Implementation
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
RequestReciepe / Service Method
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
return new ReciepeResponse() { SomeValue = "Some Value"};
Exception Stacktrace (catched by AppDomain UnhandledException)
bei System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
bei System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
bei Microsoft.Win32.Win32Native.SetEvent(SafeWaitHandle handle)
bei System.Threading.EventWaitHandle.Set()
bei System.Runtime.Remoting.Messaging.AsyncResult.SyncProcessMessage(IMessage msg)
bei System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)
bei System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.DoAsyncCall()
bei System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(Object o)
bei System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
bei System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
bei System.Threading.ThreadPoolWorkQueue.Dispatch()
bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
Hard to say for sure given the limited code, but I'd guess that something in your callback's context has been disposed before the worker thread actually got to it.
Solved.
My Wcf Service uses existing business logic. Another developer used an implementation of an custom Parallel class for multithreading purposes, which was calling:
iAsyncResult.AsyncWaitHandle.Close();
The Method who used the Parallel class wasn't designed thread-safe. After doing this, everything seems to work fine.

Ninject: How to not use the Ninject in "ashx" file and still not get an exception?

This is what i use to implement an Dependency Injection in my MVC3 project,
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel _ninjectKernel;
public NinjectControllerFactory()
{
_ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)_ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
_ninjectKernel.Bind<IUserRepository>().To<UserRepository>().InSingletonScope();
}
}
but i have a huge problem i want to use an Generic Handler an ".ashx" to implement my logic.
But i get an exception because the httphandler is not a Controller.
here is the exception:
Server Error in '/' Application.
The IControllerFactory 'Infrastructure.NinjectFactory.NinjectControllerFactory' did not return a controller for the name 'registercustomer.ashx'.
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.InvalidOperationException: The IControllerFactory 'Infrastructure.NinjectFactory.NinjectControllerFactory' did not return a controller for the name 'registercustomer.ashx'.
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:
[InvalidOperationException: The IControllerFactory 'Infrastructure.NinjectFactory.NinjectControllerFactory' did not return a controller for the name 'registercustomer.ashx'.]
System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +422803
System.Web.Mvc.<>c__DisplayClass6.<BeginProcessRequest>b__2() +49
System.Web.Mvc.<>c__DisplayClassb`1.<ProcessInApplicationTrust>b__a() +13
System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Func`1 func) +124
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +98
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +50
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8971636
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.547
Now is the question: How do i implement the work around this bug, to me to be able to an HttpHandler and still remain using the Ninject in my project?
Thanks in advance.
Due to the HttpHandler being created by the framework and there is no hook or factory method to intercept the creation of the ashx file, ninject is not able to create this object.
However you can use service locator calls or property injection from the ashx to request dependancies from the ashx code. But as far as I know, the ashx must have a default constructor, and you can then either resolve the dependancies from inside the constructor (or anywhere really) via service locator (less preferred method) or via property injection simply like this:
public class Handler
{
[Inject]
public IService Service { get; set; }
}
EDIT: also, to tell mvc to not process the ashx file you need to add this to ignore the route:
routes.IgnoreRoute("{resource}.ashx/{*pathInfo}");

Serialization Exception in compiled dll

I've inherited an ecommerce ASP.NET (c# code behind) web application. We've recently moved servers and it's proving somewhat troublesome. I have very little experience with IIS server configuration and dealing with large projects like this. Most of the problems have now been fixed, but we're experiencing problems with a crucial part, as a customer attempts to make a payment.
As the customer confirms payment, the application encounters the following error:
Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET
will serialize the session state objects, and as a result non-serializable objects or
MarshalByRef objects are not permitted. The same restriction applies if similar
serialization is done by the custom session state store in 'Custom' mode.
Stack Trace:
[SerializationException: Type 'PayerAuthentication.PayerAuthenticationServicePost' in Assembly 'PayerAuthentication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.]
System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) +7733643
System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) +258
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() +111
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) +161
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) +51
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) +410
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) +134
System.Web.Util.AltSerialization.WriteValueToStream(Object value, BinaryWriter writer) +1577
Google search results indicate I should add [Serializable] to the class declaration affected, but this is in a compiled dll to which I do not have the csproj.
The code was working fine on the previous server and I do not believe any changes have been made to the code, only to web.config - what can I do?
The sessionstate section of web.config reads <sessionState mode="StateServer" />
UPDATE1: Using Reflector, I exported the class above, made it serializable, recompiled and replaced the dll. The order process went one step further, wherepon I encountered the same error for another dll-compiled class. Once again I was able to use Reflector to see the code, and then export it, edit and recompile.
Now I have the same error occurring in:
SerializationException: Type 'System.Runtime.Remoting.Messaging.AsyncResult' in Assembly 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.]
I'm not sure I can do anything about this, as this must be part of the .net system files! Any further ideas?
UPDATE2: Ha, well I've subsequently discovered it's processing the payments correctly, but then throwing the above Unable to serialize the session state error on System.Runtime.Remoting.Messaging.AsyncResult before the user gets receipt of the transaction. Not good. Unsure how to move forward now...
UPDATE3: I tried creating a copy of the System.Runtime.Remoting.Messaging.AsyncResult class, and making it serializable but this is then leading to inconsistent accessibility problems.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Security.Permissions;
using System.Runtime.Remoting.Messaging;
[Serializable, ComVisible(true)]
public class myAsyncResult : IAsyncResult, IMessageSink
{
// Fields
private AsyncCallback _acbd;
private Delegate _asyncDelegate;
private object _asyncState;
private ManualResetEvent _AsyncWaitHandle;
private bool _endInvokeCalled;
private bool _isCompleted;
private IMessageCtrl _mc;
private IMessage _replyMsg;
// Methods
internal myAsyncResult(Message m);
//[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public virtual IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink);
private void FaultInWaitHandle();
public virtual IMessage GetReplyMessage();
public virtual void SetMessageCtrl(IMessageCtrl mc);
//[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public virtual IMessage SyncProcessMessage(IMessage msg);
// Properties
public virtual object AsyncDelegate { get; }
public virtual object AsyncState { get; }
public virtual WaitHandle AsyncWaitHandle { get; }
public virtual bool CompletedSynchronously { get; }
public bool EndInvokeCalled { get; set; }
public virtual bool IsCompleted { get; }
public IMessageSink NextSink { [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] get; }
}
Specifically, that error CS0122: 'System.Runtime.Remoting.Messaging.Message' is inaccessible due to its protection level. I can see this is because Message is an internal class. But surely I can't change the accessibility level of it as it is part of the System.Runtime namespace. And making a copy and renaming it is going to cuase the same problem again, surely?
Can anyone help me now?
FINAL UPDATE
Looks like, after all this, that it was the SSL certificate (see my answer below)
If you really wanted the code, you could try using Reflector's Class View. At the very least, it could help you verify whether or not [Serializable] was part of the problem class definition or not.
You'll need to find out if the new server is a later version than the old, or an older one. If it's an older version, then upgrade it to the newer version, and things should work.
If it's newer, then is it your code (that you have source to) that puts these non-serializable objects into session state? If so, then you can maybe create your own class to mirror the properties of the old class. Make your class serializable and put an instance of your class into session state. Make an instance of the old class when you take yours out of session state.
I now believe that this problem arose when we installed a new SSL certificate.
The new certificate had Postcode extensions which our payment merchant HSBC doesn't accept over it's CPI payment gateway.
Getting the correct SSL certificate installed seems to have finally solved this problem.
If the code was previously using just the in-memory state provider, then this could be... tricky. It is a pain point that the serialization process (via BinaryFormatter, which the database state provider uses) requires the [Serializable] attribute when the default provider doesn't.
How much of the code can you edit? Any of it? For example, can you change the code that puts things into/out-of state? You could perhaps use a separate (serializable) DTO with the necessary properties and translate between them with your own code.
Other options:
go back to the in-memory provider (and wave goodbye to a cluster)
write a provider that doesn't use BinaryFormatter
I have some thoughts on the latter, but I doubt it would be trivial
If the question is just how to make the application run without this error, the fast solution is to set the mode attribute of the sessionState element to "InProc".

Categories