I'm having some issues scoping Entity Framework using Ninject within an Azure Function.
I keep getting random object already disposed and internal EF errors, such as the following, which leads me to believe the DbContext
is being shared between threads:
I'm not sure if this is getting scoped wrong, or if i only need to be calling _kernal.Load()
once per app domain. Any insight would be greatly appreciated.
An item with the same key has already been added.
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) at System.Collections.Generic.Dictionary'2.Insert(TKey key, TValue value, Boolean add)
at System.Data.Entity.Core.Objects.ObjectStateManager.AddStateManagerTypeMetadata(EntitySet entitySet, ObjectTypeMapping mapping)
at System.Data.Entity.Core.Objects.ObjectStateManager.GetOrAddStateManagerTypeMetadata(Type entityType, EntitySet entitySet)
at System.Data.Entity.Core.Objects.ObjectStateManager.AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, String argumentName, Boolean isAdded)
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.HandleEntityAppendOnly[TEntity](Func'2 constructEntityDelegate, EntityKey entityKey, EntitySet entitySet)
at lambda_method(Closure , Shaper )
at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator'1.ReadNextElement(Shaper shaper)
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper'1.RowNestedResultEnumerator.MaterializeRow() at System.Data.Entity.Core.Common.Internal.Materialization.Shaper'1.RowNestedResultEnumerator.MoveNext() at System.Data.Entity.Core.Common.Internal.Materialization.Shaper'1.ObjectQueryNestedEnumerator.TryReadToNextElement() at System.Data.Entity.Core.Common.Internal.Materialization.Shaper'1.ObjectQueryNestedEnumerator.MoveNext() at System.Data.Entity.Internal.LazyEnumerator'1.MoveNext()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable'1 source)
at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.b__1[TResult](IEnumerable'1 sequence)
at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable'1 query, Expression queryRoot)
at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[TResult](Expression expression)
at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable'1 source, Expression`1 predicate)
at MyApp.DAO.Implementations.LoanRepository.Get(Int32 loanId) in d:\a\1\s\MyApp\MyApp.DAO\Implementations\LoanRepository.cs:line 50
at MyApp.DAO.Implementations.LoanRepository.Get(String loanGuid) in d:\a\1\s\MyApp\MyApp\Implementations\LoanRepository.cs:line 0
at MyApp.BL.Los.MyManager.d__22.MoveNext()
and
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
at System.Data.Entity.Core.Objects.ObjectContext.ReleaseConnection() at System.Data.Entity.Core.Common.Internal.Materialization.Shaper'1.Finally() at System.Data.Entity.Core.Common.Internal.Materialization.Shaper'1.SimpleEnumerator.Dispose() at System.Data.Entity.Internal.LazyEnumerator`1.Dispose() at MyApp.DAO.Implementations.PromotionRepository.getAllActivePromotions(Int32 LoanID) in d:\a\1\s\MyApp\MyApp.DAO\Implementations\PromotionRepository.cs:line 56 at MyApp.DAO.Implementations.LoanRepository.Get(Int32 loanId) in d:\a\1\s\MyApp\MyApp.DAO\Implementations\LoanRepository.cs:line 204 at MyApp.DAO.Implementations.LoanRepository.Get(String loanGuid) in d:\a\1\s\MyApp\MyApp.DAO\Implementations\LoanRepository.cs:line 0 at MyApp.BL.Los.MyManager.d__22.MoveNext() in d:\a\1\s\MyApp\MyApp.BL.Los\MyManager.cs:line 63
Ninject Configuration
public class NinjectBindings : NinjectModule
{
public override void Load()
{
Bind<MyDBContext>().ToSelf().InSingletonScope().WithConstructorArgument("connectionString", "name=MyDB");
}
}
Azure Function
[FunctionName("ProcessData")]
public static async Task ProcessData([QueueTrigger("myqueue", Connection = "AzureWebJobsStorage")]string message, int dequeueCount, ILogger log, ExecutionContext context)
{
using (StandardKernel _kernal = new StandardKernel())
{
_kernal.Load(Assembly.GetExecutingAssembly());
// do work
}
}
Based on your description, I used your code and found that the following code could work as expected.
using (StandardKernel _kernal = new StandardKernel())
{
_kernal.Load(Assembly.GetExecutingAssembly());
// do work
BruceDbContext ctx = _kernal.Get<BruceDbContext>();
var todoitem = ctx.TodoItems.FirstOrDefault();
log.Info(JsonConvert.SerializeObject(todoitem));
}
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. System.Data.Entity.Core.Common.Internal.Materialization.Shaper'1.SimpleEnumerator.Dispose() at System.Data.Entity.Internal.LazyEnumerator`1.Dispose()
I assumed that the errors are thrown from your operations when using EF. You need to make sure access the lazy-loading navigation properties before you dispose the DbContext. Here is a similar issue, you could refer to it. In general, you need to check your code and try to find the specific code line which causes this issue based on the full StackTrace of the exception. Or you could update your question with more details about the error and the code you used for us to narrow this issue.
Additionally, Azure Functions do not support DI that is similar to the way for webjobs. Also, I found the github issue. Moreover, you could follow Proper Dependency injection in Azure Functions on function level with scoped services! and Dependency injection in Azure Functions on function level.
I'm guessing you called the context outside the using statement. The life of the context object you create using the kernel is only for the lifespan of the using statement. However,you really don't need DI in the example you provided. If you are instantiating the Kernel inside the function and then accessing the context from that why not just create a context instead. If your set on this then you need to pass the Kernal as a parameter of the function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With