I am trying to implement an audit trail on MVC project , by overriding the context by adding another peice of functionality (to so the audit).
The overriding of SaveChanges works fine, however the problem I have is with SaveChangesAsync.
Here is part the code from the context
public override Task<int> SaveChangesAsync()
{
throw new InvalidOperationException("User ID must be provided");
}
public override int SaveChanges()
{
throw new InvalidOperationException("User ID must be provided");
}
public async Task<int> SaveChangesAsync(int userId)
{
DecidSaveChanges(userId);
return await this.SaveChangesAsync(CancellationToken.None);
}
public int SaveChanges(int userId)
{
DecidSaveChanges(userId);
return base.SaveChanges();
}
The problem I have is with my controller
await db.SaveChangesAsync(1);
1 being a dummy user. I get the following error.
Error 1 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task<System.Web.Mvc.ActionResult>'.
do you know what I am doing wrong here? and how to fix it?. I am using EF6 and MVC5
do you know what I am doing wrong here?
Yes, just look at your compiler error message:
The 'await' operator can only be used within an async method.
So, the controller action (that contains the call to SaveChangesAsync(1)) needs to be async.
and how to fix it?
Yes, just look at your compiler error message:
Consider marking this method with the 'async' modifier and changing its return type to 'Task<System.Web.Mvc.ActionResult>'.
So, you fix it by making the controller action async and changing its return type from ActionResult to Task<ActionResult>.
Related
I'm a SQL guy who's tinkering with Web API and Entity Framework 6 and I keep receiving the error "The operation cannot be completed because the DbContext has been disposed" when I my code is:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
}
But if change my code around a little and try this it works:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
readonly coreEntities coreDB = new coreEntities();
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
My question is why does the second one work but not the first? Is it better practice to have a global connection string or call DBContext explicitly each time?
Your are getting error because you are returning the IQueryable for which Entity framework has yet not executed the query and DbContext has been disposed when that query needs to be executed.
Remember Entity framework will not execute query until collection is initialized or any method that does not support deferred execution. Visit this link for list of Linq deferred execution supported method.
why does the second one work but not the first?
In first code snippet you are returning an instance of IQuerable which has not executed DbQuery and then after it just fires dispose on your context (coreDB). So then after whenever your code iterate over the collection it tries to fire DbQuery but finds that context has already been destroyed so you are getting an error.
In second case when ever you are iterating over the collection coreDB context must be alive so you are not getting an error.
Is it better practice to have a global connection string or call DBContext explicitly each time?
Answer to this question is based on developers taste or his own comforts. You can use your context wrapped within using statements as below:
public IList GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType.ToList();
}
}
As shown in above code snippet if you would use ToList before returning it would execute query before your coreDB got destroyed. In this case you will have to make sure that you returned materialized response (i.e. returned response after executing the DbQuery).
Note: I have noticed most of the people choose the second way. Which targets context as an instance field or property.
I'm trying to mock out a class in Moq with this method:
public virtual void Init(Context context, IPrincipal user)
{
_context = context;
_user = user;
var u = _context.Users.Include("Foo").First(x => x.Login == _user.Identity.Name);
My code that is calling this looks like this:
mockLoginState.CallBase = false;
mockLoginState.Setup(x => x.Init(It.IsAny<Context>(), It.IsAny<IPrincipal>()));
I don't want this method to actually be called because it will hit the database. From what I understand about Moq, this should be stubbed over because it's virtual and the code in the function shouldn't run, but this is not what I'm experiencing. Basically my problem is exactly as is described here:
"Short circuiting" void methods with Moq?
but the answer to that question didn't work for me.
How do I stop the function from being called?
Your code above looks correct. Are you sure you're calling the Init method on the Mock object? Have you tried using MockBehavior.Strict when constructing your Mock<LoginState>? This should throw an exception if any method that you haven't explicitly set up gets called.
Otherwise, you might have to provide more of your code for context.
Joe Taylor's answer was a clue.
Apparently the Init function was being called in the constructor of my LoginState object. When I created the Mock<LoginState> it was being called, and therefor my override was happening too late.
For more information on dealing with this constraint, see this question:
Use Moq to mock Constructor?
By leveraging the Testing with async queries section of the Testing with a Mocking Framework article on MSDN, I've been able to create many successfully passing tests.
Here's my test code, which uses NSubstitute for mocks:
var dummyQueryable = locations.AsQueryable();
var mock = Substitute.For<DbSet<Location>, IDbAsyncEnumerable<Location>, IQueryable<Location>>();
((IDbAsyncEnumerable<Location>)mock).GetAsyncEnumerator().Returns(new TestDbAsyncEnumerator<Location>(dummyQueryable.GetEnumerator()));
((IQueryable<Location>)mock).Provider.Returns(new TestDbAsyncQueryProvider<Location>(dummyQueryable.Provider));
((IQueryable<Location>)mock).Expression.Returns(dummyQueryable.Expression);
((IQueryable<Location>)mock).ElementType.Returns(dummyQueryable.ElementType);
((IQueryable<Location>)mock).GetEnumerator().Returns(dummyQueryable.GetEnumerator());
sut.DataContext.Locations = mock;
var result = await sut.Index();
result.Should().BeView();
sut.Index() doesn't do much, but it makes the following query:
await DataContext.Locations
.GroupBy(l => l.Area)
.ToListAsync());
This works fine until I add a projection into the query:
await DataContext.Locations
.GroupBy(l => l.Area)
.Select(l => new LocationsIndexVM{ Area = l.Key }) // added projection
.ToListAsync());
which results in this exception:
System.InvalidOperationException
The source IQueryable doesn't implement IDbAsyncEnumerable<LocationsIndexVM>. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
at System.Data.Entity.QueryableExtensions.AsDbAsyncEnumerable(IQueryable`1 source)
at System.Data.Entity.QueryableExtensions.ToListAsync(IQueryable`1 source)
at Example.Web.Controllers.HomeController.<Index>d__0.MoveNext() in HomeController.cs: line 25
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Example.Test.Web.Controllers.HomeControllerShould.<TempTest>d__4.MoveNext() in HomeControllerShould.cs: line 71
UPDATE: I've uploaded a small, simple solution that reproduces this problem.
Can anyone provide an example of what is required to unit test a query that is both async and contains a .Select() projection?
So I did a bit of digging, and the issue is to do with the way the TestDbAsyncEnumerable<T> exposes the IQueryProvider. My best guess as to the reasoning is below, and the solution below that.
TestDbAsyncEnumerable<T> inherits from EnumerableQuery<T>, which in turn inherits from IQueryable<T>, and explicitly implements the Provider property of this interface:
IQueryProvider IQueryable.Provider { get ... }
Given that it's implemented explicitly, I am assuming that the LINQ internals explicitly cast a type before trying to get the Provider:
((IQueryable<T>)source).Provider.CreateQuery(...);
I don't have a source on hand (and can't be bothered looking for one), but I believe the type binding rules are different for explicit implementations; essentially, the Provider property on your TestDbAsyncEnumerable<T> is not considered to be an implementation of IQueryable<T>.Provider as an explicit one exists further up the chain, so your TestDbAsyncQueryProvider<T> is never returned.
The fix for this is to make TestDbAsyncEnumerable<T> also inherit IQueryable<T> and explicitly implement the Provider property, as below (adjusted from the MSDN article you linked):
public class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression) : base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}
I'm working on trying to get an AsyncController to work in OrchardProject. The current version I'm using is 2.2.4.9.0.
I've had 2 people eyeball my code: http://www.pastie.org/2117952 (AsyncController) which works fine in a regular MVC3 vanilla application.
Basically, I can route to IndexCompleted, but I can't route to Index. I am going to assume i'm missing something in the Autofac configuration of the overall project.
I think the configuration is in the global.asax: http://pastie.org/2118008
What I'm looking for is some guidance on if this is the correct way to implement autofac for AsyncControllers, or if there is something/someplace else I need to implement/initialize/etc.
~Dan
Orchard appears to register its own IActionInvoker, called Orchard.Mvc.Filters.FilterResolvingActionInvoker.
This class derives from ControllerActionInvoker. At a guess, in order to support async actions, it should instead derive from AsyncControllerActionInvoker.
Hope this helps!
Nick
The Autofac setup looks ok, and as long as you can navigate to something I cannot say that your assumption makes sense. Also, the pattern you are using for initialization in global.asax is used by others too.
The AsyncController requires that async methods come in pairs, in your case IndexAsync & IndexCompleted. These together represent the Index action. When you say you can navigate to IndexCompleted, do you mean that you open a url "..../IndexCompleted"?
Also, and this I cannot confirm from any documentation, but I would guess that AsyncController requires that all actions are async. Thus, your NewMessage action causes trouble and should be converted to an async NewMessageAsync & NewMessageCompleted pair.
I did too needed to have AsyncController which I easily changed FilterResolvingActionInvoker to be based on AsyncControllerActionInvoker instead of ControllerActionInvoker.
But there was other problems because of automatic transaction disposal after completion of request. In AsyncController starting thread and the thread that completes the request can be different which throws following exception in Dispose method of TransactionManager class:
A TransactionScope must be disposed on the same thread that it was created.
This exception is suppressed without any logging and really was hard to find out. In this case session remains not-disposed and subsequent sessions will timeout.
So I made dispose method public on ITransactionManager and now in my AsyncController, whenever I need a query to database I wrap it in:
using (_services.TransactionManager) {
.....
}
new TransactionManager :
public interface ITransactionManager : IDependency, IDisposable {
void Demand();
void Cancel();
}
public class TransactionManager : ITransactionManager {
private TransactionScope _scope;
private bool _cancelled;
public TransactionManager() {
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void Demand() {
if (_scope == null) {
Logger.Debug("Creating transaction on Demand");
_scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = IsolationLevel.ReadCommitted
});
_cancelled = false;
}
}
void ITransactionManager.Cancel() {
Logger.Debug("Transaction cancelled flag set");
_cancelled = true;
}
void IDisposable.Dispose() {
if (_scope != null) {
if (!_cancelled) {
Logger.Debug("Marking transaction as complete");
_scope.Complete();
}
Logger.Debug("Final work for transaction being performed");
try {
_scope.Dispose();
}
catch {
// swallowing the exception
}
Logger.Debug("Transaction disposed");
}
_scope = null;
}
}
Please notice that I have made other small changes to TransactionManager.
I tried the AsyncControllerActionInvoker route as well to no avail. I would get intermittent errors from Orchard itself with the following errors:
Orchard.Exceptions.DefaultExceptionPolicy - An unexpected exception was caught
System.TimeoutException: The operation has timed out.
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.Async.ReflectedAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3f.<BeginInvokeAsynchronousActionMethod>b__3e(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
NHibernate.Util.ADOExceptionReporter - While preparing SELECT this_.Id as Id236_2_, this_.Number as Number236_2_,...<blah blah blah>
NHibernate.Util.ADOExceptionReporter - The connection object can not be enlisted in transaction scope.
So I don't think just wrapping your own database calls with a transaction object will help. The innards of Orchard would have to modified as well.
Go vote for this issue if you want AsyncControllers supported in Orchard:
https://orchard.codeplex.com/workitem/18012
I am toying around to learn how to unit test ASP.NET MVC controller actions. Specifically I'm trying to mock the ControllerContext so that I can test an action that accesses HttpContext.Current.User.Identity.Name.
I'm using Moq.
Things were going pretty well until I turned on MockBehavior.Strict. I knew that this would throw an exception if the code failed to call the thing that I marked Verifiable. Apparently, it will also throw an exception if "extra" methods where I don't provide a setup (like IsChildAction) don't get called.
[TestMethod]
public void Index_Get_AccessesUserIdentityName()
{
// Arrange
var mock = new Mock<ControllerContext>(MockBehavior.Strict);
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns("treycarroll").Verifiable();
HomeController controller = new HomeController();
controller.ControllerContext = mock.Object;
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
mock.Verify();
...
}
Here is the Controller action that I'm testing:
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!"+User.Identity.Name;
return View();
}
The exception is triggered when the return View(); statement is executed. The error message tells me that I need a setup method for the call to IsChildAction so I updated my test class to this:
[TestMethod]
public void Index_Get_AccessesUserIdentityName()
{
// Arrange
var mock = new Mock<ControllerContext>(MockBehavior.Strict);
string expectedUserName = "treycarroll";
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(expectedUserName).Verifiable();
mock.SetupGet(m => m.IsChildAction).Returns(true).Verifiable();
HomeController controller = new HomeController();
controller.ControllerContext = mock.Object;
// Act
ViewResult result = controller.Index() as ViewResult;
string actualUserName = controller.ControllerContext.HttpContext.User.Identity.Name;
// Assert
mock.Verify();
Assert.AreEqual(actualUserName, expectedUserName);
Assert.IsNotNull(result);
}
...
After which I get a similar error about no setup method for ControllerContext.RouteData. By process of elimination I could wind up adding Setup methods for all the missing calls, but this doesn't seem right. Maybe I'm misunderstanding the use of MockBehavior.Strict, but I thought that you turn this on in order to avoid getting default values for your properties (such as null for the User object that I want to inspect). What am I missing here?
A strict mock will fail immediately if anything differs from the expectations. So this means, that if any method call not specified in expectation will fail. On the other hand, a non-strict mock ignore, such calls