We have the following ASP.NET code (I've cut it down for clarity):
var httpContent = new StringContent(postData, Encoding.UTF8);
using (var client = this.GetClient(url, contentType))
{
using (var response = await client.PostAsync(url, httpContent).ConfigureAwait(true);
{
ret.Response = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
}
}
'client' is an HttpClient object when run in PROD, but in our Unit Test it's:
MockRepository.GenerateMock<IHttpClient>();
Notice that we use .ConfigureAwait(true). The reason for this is that we store objects in the HttpContext.Current.Items collection (this probably isn't ideal, but it is what it is). If we didn't persist the Thread's Context then HttpContext.Current returns as null.
This all works as expected when run in PROD.
However, when running the Unit Test, execution of the following line loses Context and HttpContext.Current becomes null:
await response.Content.ReadAsStringAsync().ConfigureAwait(true);
Note - the proceeding call of PostAsync on the Stub maintains the Context.
So how do we program the Stub?
using (var httpResponse = new HttpResponseMessage())
{
httpResponse.Content = new StringContent(msg, Encoding.UTF8);
httpResponse.StatusCode = HttpStatusCode.OK;
this.FakeHttpClient
.Stub(i => i.PostAsync("", "")).IgnoreArguments()
.Return(Task.FromResult(this.httpResponse)).Repeat.Once();
// Act
}
When the Test runs, the data is returned so the fake objects behave perfectly EXCEPT that the ReadAsStringAsync().ConfigureAwait(true) destroys the Context.
StringContent can't be mocked directly as it doesn't have a parameterless constructor. I created a wrapper class to see if I could mock that, but when I had the following code:
var fakeStringContent = MockRepository.GenerateStub<Wrap>();
fakeStringContent
.Stub(fsc => fsc.ReadAsStringAsync())
.Return(Task.FromResult("<response/>"))
.Repeat.Once();
Then there was an Exception thrown on the .Stub(...) line which was:
System.InvalidOperationException: 'The async operation did not return
a System.Threading.Tasks.Task object.'
Related
To be able to call Microsoft.Graph API on my .Net MAUI app, I need to get an access token. I followed this documentation:
https://learn.microsoft.com/en-us/graph/tutorials/dotnet?tabs=aad&tutorial-step=3
And here is my code:
internal class GraphHelper
{
private static string[] _graphUserScopes = new[] { "https://graph.microsoft.com/.default" };
// User auth token credential
private static DeviceCodeCredential? _deviceCodeCredential;
// Client configured with user authentication
private static GraphServiceClient? _userClient;
public static void InitializeGraphForUserAuth(Func<DeviceCodeInfo, CancellationToken, Task> deviceCodePrompt)
{
string adTenantId = "MY TENANT ID";
string adClientId = "MY CLIENT ID";
_deviceCodeCredential = new DeviceCodeCredential(deviceCodePrompt,
adTenantId, adClientId);
_userClient = new GraphServiceClient(_deviceCodeCredential, _graphUserScopes);
}
public static async Task<string> GetUserTokenAsync()
{
// Ensure credential isn't null
_ = _deviceCodeCredential ??
throw new NullReferenceException("Graph has not been initialized for user auth");
// Ensure scopes isn't null
_ = _graphUserScopes ?? throw new ArgumentNullException("Argument 'scopes' cannot be null");
// Request token with given scopes
TokenRequestContext context = new TokenRequestContext(_graphUserScopes);
AccessToken response = default;
try
{
response = await _deviceCodeCredential.GetTokenAsync(context);
}
catch (Exception ex)
{
}
return response.Token;
}
}
Call to await _deviceCodeCredential.GetTokenAsync(context) never comes back. And only in about 10 minutes the following exception is thrown:
Azure.Identity.AuthenticationFailedException: DeviceCodeCredential authentication failed: Verification code expired before contacting the server
I would like to know how I can diagnose and/or fix this problem.
Call to await ... never comes back.
await that never returns is a deadlock.
To find out where the problem starts, and fix it:
Put a breakpoint at start of GetUserTokenAsync.
Look at call stack. Check each method in call stack to see if it is declared async (you'll have to go to source code of each method).
What is the first method you encounter that is NOT declared async?
Look at the async method it calls: is there an await in front of that call? If not, that is your problem.
Starting a chain of async/await calls from a non-async context can cause a thread deadlock.
Typically happens on UI thread. Most common mistake is attempting to call inside a constructor.
IF it is the problem I describe, try adding await.
Build. If no compile error, it should now work.
If there is compile error at that await, a fix is to create an async context to do the await in.
To await on UI thread (MainThread):
// An alternative is "MainThread.BeginInvokeOnMainThread".
Dispatcher.Dispatch(async () =>
{
... = await ...;
// Code after your await line goes here.
}
// DO NOT have any code here, unless it is okay to run it BEFORE the dispatched await finishes.
To await on a background thread:
Task.Run(async () =>
... same logic as above ...
I ran into an issue where I was intermittently receiving an error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
whenever trying to attach an entity to the DbContext.
UPDATE: Original post is below and is TL;DR. So here is a simplified version with more testing.
First I get the Documents collection. There are 2 items returned in this query.
using (UnitOfWork uow = new UnitOfWork())
{
// uncomment below line resolves all errors
// uow.Context.Configuration.ProxyCreationEnabled = false;
// returns 2 documents in the collection
documents = uow.DocumentRepository.GetDocumentByBatchEagerly(skip, take).ToList();
}
Scenario 1:
using (UnitOfWork uow2 = new UnitOfWork())
{
// This errors ONLY if the original `uow` context is not disposed.
uow2.DocumentRepository.Update(documents[0]);
}
This scenario works as expected. I can force the IEntityChangeTracker error by NOT disposing the original uow context.
Scenario 2:
Iterate through the 2 items in the documents collection.
foreach (Document document in documents)
{
_ = Task.Run(() =>
{
using (UnitOfWork uow3 = new UnitOfWork())
{
uow3.DocumentRepository.Update(document);
});
}
}
Both items fail to attach to the DbSet with the IEntityChangeTracker error. Sometimes one succeeds and only one fails. I assume this might be to do with the exact timings of the Task Scheduler. But even if they are attaching concurrently, they are different document entities. So they shouldn't be being tracked by any other context. Why am I getting the error?
If I uncomment ProxyCreationEnabled = false on the original uow context, this scenario works! So how are they still being tracked even thought the context was disposed? Why is it a problem that they are DynamicProxies, even though they are not attached to or tracked by any context.
ORIGINAL POST:
I have an entity object called Document, and it's related entity which is a collection of DocumentVersions.
In the code below, the document object and all related entities including DocumentVersions have already been eagerly loaded before being passed to this method - which I will demonstrate after.
public async Task PutNewVersions(Document document)
{
// get versions
List<DocumentVersion> versions = document.DocumentVersions.ToList();
for (int i = 0; i < versions.Count; i++)
{
UnitOfWork uow = new UnitOfWork();
try
{
versions[i].Attempt++;
//... make some API call that succeeds
versions[i].ContentUploaded = true;
versions[i].Result = 1;
}
finally
{
uow.DocumentVersionRepository.Update(versions[i]); // error hit in this method
uow.Save();
}
}
}
The Update method just attaches the entity and changes the state. It is part of a GenericRepository class that all my Entity Repositories inherit from:
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate); // error is hit here
context.Entry(entityToUpdate).State = EntityState.Modified;
}
The document entity, and all related entities are loaded eagerly using a method in the Document entity repository:
public class DocumentRepository : GenericRepository<Document>
{
public DocumentRepository(MyEntities context) : base(context)
{
this.context = context;
}
public IEnumerable<Document> GetDocumentByBatchEagerly(int skip, int take)
{
return (from document in context.Documents
.Include(...)
.Include(...)
.Include(...)
.Include(...)
.Include(d => d.DocumentVersions)
.AsNoTracking()
orderby document.DocumentKey descending
select document).Skip(skip).Take(take);
}
}
The method description for .AsNoTracking() says that "the entities returned will not be cached in the DbContext". Great!
Then why does the .Attach() method above think that this DocumentVersion entity is already referenced in another IEntityChangeTracker? I am assuming this means it is referenced in another DbContext, i.e: the one calling GetDocumentByBatchEagerly(). And why does this issue only present intermittently? It seems that it happens less often when I am stepping through the code.
I resolved this by adding the following line to the above DocumentRepository constructor:
this.context.Configuration.ProxyCreationEnabled = false;
I just don't understand why this appears to resolve the issue.
It also means if I ever want to use the DocumentRepository for something else and want to leverage change tracking and lazy loading, I can't. There doesn't seem to be a 'per query' option to turn off dynamic proxies like there is with 'as no tracking'.
For completeness, here is how the 'GetDocumentsByBatchEagerly' method is being used, to demonstrate that it uses it's own instance of UnitOfWork:
public class MigrationHandler
{
UnitOfWork uow = new UnitOfWork();
public async Task FeedPipelineAsync()
{
bool moreResults = true;
do
{
// documents retrieved with AsNoTracking()
List<Document> documents = uow.DocumentRepository.GetDocumentByBatchEagerly(skip, take).ToList();
if (documents.Count == 0) moreResults = false;
skip += take;
// push each record into TPL Dataflow pipeline
foreach (Document document in documents)
{
// Entry point for the data flow pipeline which links to
// a block that calls PutNewVersions()
await dataFlowPipeline.DocumentCreationTransformBlock.SendAsync(document);
}
} while (moreResults);
dataFlowPipeline.DocumentCreationTransformBlock.Complete();
// await completion of each block at the end of the pipeline
await Task.WhenAll(
dataFlowPipeline.FileDocumentsActionBlock.Completion,
dataFlowPipeline.PutVersionActionBlock.Completion);
}
}
I'm using Delegate Factories to create an object using a static .Create() function because the creation of the object is somewhat heavy and doesn't belong in a constructor. This object (UserToken) is used on every Mvc Controller.
I've noticed in my code example , when registering with an AutoFac delegate function to create UserToken the UserToken.Create() is called multiple times per request. I expected it to only be called once per request and having the resulting UserToken stored and re-used by the builder. What am I missing here? I only want UserToken.Create called once per request.
builder.Register<Func<HttpContextBase, IUnitOfWork, UserAccountToken>>(
c => {
var db = c.Resolve<IUnitOfWork>();
var hc = c.Resolve<HttpContextBase>();
return (context, database) => { return UserAccountToken.Create(hc, db); };
}).InstancePerRequest();
builder.Register<UserAccountToken>(
c => {
var db = c.Resolve<IUnitOfWork>();
var hc = c.Resolve<HttpContextBase>();
return UserAccountToken.Create(hc, db);
}).As<IUserAccountToken>().InstancePerRequest();
I'm not sure how you are using the registered type Func<HttpContextBase, IUnitOfWork, UserAccountToken>. You're basically returning a function that receives two parameters which you are never using. (context and database).
I'm assuming to create an IUserAccountToken you only need to call UserAccountToken.Create() and pass its corresponding parameters. If that is the case you only need the second registration.
It would only be:
builder.Register<UserAccountToken>(c =>
{
var db = c.Resolve<IUnitOfWork>();
var hc = c.Resolve<HttpContextBase>();
return UserAccountToken.Create(hc, db);
})
.As<IUserAccountToken>()
.InstancePerRequest();
(Problem solved)
I have an MVC application, in my Action:
First case: Task never started.
public ActionResult Insert(NewsManagementModel model) {
//Do some stuff
//Insert history
//new object NewsHistoryDto as the parameter
Task.Factory.StartNew(() => InsertNewsHistory(new NewsHistoryDto {
UserId = 1234,
Time = DateTime.Now,
Id = 1234
}));
return RedirectToAction("Index", "NewsManagement");
}
Second case: Task run normally
public ActionResult Insert(NewsManagementModel model) {
//Do some stuff
//Insert history
//object NewsHistoryDto was declared outside
var history = new NewsHistoryDto {
UserId = 1234,
Time = DateTime.Now,
Id = 1234
};
Task.Factory.StartNew(() => InsertNewsHistory(history));
return RedirectToAction("Index", "NewsManagement");
}
My question is: When Task.Factory.StartNew and i put a method into it, the parameter of that method (object) must be declare outside??? Because when i write shortly like the first case, i put the "new" keyword in the parameter and the task never run.
Reason: In action, i wanna return view as soon as possible, any other stuff not related to that view will be excute in a task and client no need to wait for completed.
I'm very sorry about my bad english :)
Updated 1:
Thanks Panagiotis Kanavos, I used QueueBackgroundWorkItem but the problem still the same, if i declare the object outside, this method run normally. But when i use new keyword inside the parameter, this method never run. No exception, no errors. Can anyone explain to me how this possible :(
Updated 2:
I try two case:
First:
HostingEnvironment.QueueBackgroundWorkItem(delegate {
var handler = m_bussinessHandler;
handler.InsertNewsHistoryAsync(new NewsHistoryDto {
UserId = UserModel.Current.UserId,
Time = DateTime.Now,
Id = newsId
});
});-> still doens't works
Second:
var history = new NewsHistoryDto {
UserId = UserModel.Current.UserId,
Time = DateTime.Now,
Id = newsId
};
HostingEnvironment.QueueBackgroundWorkItem(delegate {
var handler = m_bussinessHandler;
handler.InsertNewsHistoryAsync(history);
});-> works normally
So where is the problem here??? That's not about m_bussinessHandler because i copied.
Updated 3: I found the reason.
The reason is UserModel.Current, this is an object in HttpContext.Current.Session["UserModel"], in this case when i call async method, when this method actually excute, it can access to HttpContext.Current which is null. So i can solve this problem by declare object outside to store data and pass it into method or I capture UserModel.Current and pass it into this method to use UserModel.Current.UserId.
My problem actually solved, thanks everyone for helping me, especially Panagiotis Kanavos.
As it is, your code returns to the caller before the task finishes. The task may not have even started executing by the time the code returns, especially if you are debugging the method. The debugger freezes all threads and executes only one of them step by step.
Moreover, .NET's reference capture means that when you use m_businessHandler you capture a reference to the m_businessHandler field, not its value. If your controller gets garbage collected, this will result in a NullReferenceException. To avoid this you have to make a copy of the field's value inside your lambda.
You should write a proper asynchronous method instead, that returns to the user only when the asynchronous operation completes:
public async Task<ActionResult> Insert(NewsManagementModel model) {
//Do some stuff
//Insert history
//new object NewsHistoryDto as the parameter
await Task.Run(() =>
var handler=m_bussinessHandler;
handler.InsertNewsHistory(new NewsHistoryDto {
UserId = 1234,
Time = DateTime.Now,
Id = 1234
}));
return RedirectToAction("Index", "NewsManagement");
}
Task.Run or Task.Factory.StartNew are roughly equivalent.
Even so, it's not a good idea to use Task.Run to fake asynchronous execution - you simply switched execution from one server thread to another. You should use asynchronous methods all the way down to the database, eg. use ExecuteNonQueryAsync if you are using ADO.NET or SaveChangesAsync in Entity Framework. This way no thread executes or blocks while the code waits for the database call to complete.
The compiler also takes care of capturing the field's value so you don't need to copy anything. The resulting code is a lot cleaner:
public async Task<ActionResult> Insert(NewsManagementModel model) {
//Do some stuff
//Insert history
//new object NewsHistoryDto as the parameter
await m_bussinessHandler.InsertNewsHistoryAsync(new NewsHistoryDto {
UserId = 1234,
Time = DateTime.Now,
Id = 1234
};
return RedirectToAction("Index", "NewsManagement");
}
If you do want the operation to run in the background and return to the client immediately, you can use QueueBackgroundWorkItem which starts a new Task and registers it with IIS. In this case you have to copy the field's value again:
public ActionResult Insert(NewsManagementModel model) {
//Do some stuff
//Insert history
//new object NewsHistoryDto as the parameter
HostingEnvironment.QueueBackgroundWorkItem(ct=>
var handler=m_bussinessHandler;
handler.InsertNewsHistoryAsync(new NewsHistoryDto {
UserId = 1234,
Time = DateTime.Now,
Id = 1234
});
return RedirectToAction("Index", "NewsManagement");
}
IIS can still cancel the task, eg when the application pool recycles. Before it does so, it will notify the task through the CancellationTask passed to the lambda (the ct parameter). If the task doesn't finish in time, IIS will go on and abort the thread anyway. A long running task should check the token periodically.
You can pass the cancellation token to most asynchronous methods, eg ExecuteNonQueryAsync(CancellationToken). This will cause the IO operation to cancel as soon as it's safe to do so instead of waiting until the remote server responds.
Scott Hanselman has a nice article describing all the ways available to rung tasks and even scheduled jobs in the background in How to run Background Tasks in ASP.NET
Is your m_bussinessHandler an instance field? Because it can got disposed after you finished the actions.
I am developing an application using Breezejs, EF 4.4, MVC4, WebAPI and OData. When breeze makes a call to the Metadata ActionMethod the result is null. We use a code first approach and therefore do not have an EDMX file so I think the error comes about when breeze tries to "re-create" the EDMX in some capacity and it can't. See below for source code where try catch produces an exception.
Example of runtime code where execution fails.
// ~/odata/Analysis/Metadata
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
I have managed to include my project into the Breezejs repository located on GitHub. The exception occurs on the line with code "EdmxWriter.WriteEdmx(dbContext, xwriter);". I'm not sure what the issue is however the contents of the "WriteEdmx" method are below as well.
Does anyone have any idea what is going on? The only thing that I can think of is that the context that I am using is inherited from a base context which then inherits from DbContext, but other than that I am completely puzzled and at a stand still. Note: I have read that inheritance is not yet supported in breeze, but I'm not sure if that includes the contexts classes and in a separate test case I used a context that inherited from DbContext and I still received the same error.
private static String GetMetadataFromDbContext(Object context) {
var dbContext = (DbContext) context;
XElement xele;
try {
using (var swriter = new StringWriter()) {
using (var xwriter = new XmlTextWriter(swriter)) {
EdmxWriter.WriteEdmx(dbContext, xwriter);
xele = XElement.Parse(swriter.ToString());
}
}
} catch (Exception e) {
if (e is NotSupportedException) {
// DbContext that fails on WriteEdmx is likely a DataBase first DbContext.
return GetMetadataFromObjectContext(dbContext);
} else {
throw;
}
}
var ns = xele.Name.Namespace;
var conceptualEle = xele.Descendants(ns + "ConceptualModels").First();
var schemaEle = conceptualEle.Elements().First(ele => ele.Name.LocalName == "Schema");
var xDoc = XDocument.Load(schemaEle.CreateReader());
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
// This is needed because the raw edmx has a different namespace than the CLR types that it references.
xDoc = UpdateCSpaceOSpaceMapping(xDoc, objectContext);
return XDocToJson(xDoc);
}
"WriteEdmx"
// Summary:
// Uses Code First with the given context and writes the resulting Entity Data
// Model to the given writer in EDMX form. This method can only be used with
// context instances that use Code First and create the model internally. The
// method cannot be used for contexts created using Database First or Model
// First, for contexts created using a pre-existing System.Data.Objects.ObjectContext,
// or for contexts created using a pre-existing System.Data.Entity.Infrastructure.DbCompiledModel.
//
// Parameters:
// context:
// The context.
//
// writer:
// The writer.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edmx")]
public static void WriteEdmx(DbContext context, XmlWriter writer);
UPDATE: A downgrade from EF 4.4 to EF 4.1 seemed to have solved this problem. An upgrade to EF 5.0 or the nightly build might also do the same.
That's the best I can do regarding this obscure issue. #baffled