One of the testers in my company found an error on my ASP.Net MVC 3 solution, that I believe is pretty common.
One post to the server it can handle. But if you send a lot of posts, like a denial-of-service attack (DoS attack) it thrown an exception:
Server Error in '/' Application.
Initializing[UseSoft.ProdMaster.Domain.Entities.CustomerOrderHeader#567]-failed to lazily initialize a collection of role: UseSoft.ProdMaster.Domain.Entities.CustomerOrderHeader.CustomerOrderLines, no session or session was closed
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: NHibernate.LazyInitializationException: Initializing[UseSoft.ProdMaster.Domain.Entities.CustomerOrderHeader#567]-failed to lazily initialize a collection of role: UseSoft.ProdMaster.Domain.Entities.CustomerOrderHeader.CustomerOrderLines, no session or session was closed
Source Error:
Line 880:
Line 881:
Line 882: return
Line 883: Json(
Line 884: new
Source File: C:\Projects\DavidPM\Hosts\ProdMaster.Hosts.Web\Areas\Sales\Controllers\CustomerOrderController.cs Line: 882
Stack Trace:
[LazyInitializationException: Initializing[UseSoft.ProdMaster.Domain.Entities.CustomerOrderHeader#567]-failed to lazily initialize a collection of role: UseSoft.ProdMaster.Domain.Entities.CustomerOrderHeader.CustomerOrderLines, no session or session was closed]
NHibernate.Collection.AbstractPersistentCollection.ThrowLazyInitializationException(String message) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Collection\AbstractPersistentCollection.cs:484
NHibernate.Collection.AbstractPersistentCollection.ThrowLazyInitializationExceptionIfNotConnected() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Collection\AbstractPersistentCollection.cs:474
NHibernate.Collection.AbstractPersistentCollection.Initialize(Boolean writing) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Collection\AbstractPersistentCollection.cs:465
NHibernate.Collection.AbstractPersistentCollection.Read() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Collection\AbstractPersistentCollection.cs:264
NHibernate.Collection.Generic.PersistentGenericBag`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Collection\Generic\PersistentGenericBag.cs:142
System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +63
System.Linq.Buffer`1..ctor(IEnumerable`1 source) +217
System.Linq.Enumerable.ToArray(IEnumerable`1 source) +78
UseSoft.ProdMaster.Hosts.Web.Areas.Sales.Controllers.CustomerOrderController.SaveOrUpdateOrderLines(CustomerOrderModel customerOrderModel) in C:\Projects\DavidPM\Hosts\ProdMaster.Hosts.Web\Areas\Sales\Controllers\CustomerOrderController.cs:882
lambda_method(Closure , ControllerBase , Object[] ) +162
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +208
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
System.Web.Mvc.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12() +55
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +191
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
System.Web.Mvc.Controller.ExecuteCore() +116
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8862381
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
The is described bellow:
Click in a button many times. Or click once and press enter, it will send a lot of requests to the server "Post".
The Model Basically is:
public class CustomerOrderModel
{
public CustomerOrderModel()
{
this.CustomerOrderLines = new List<CustomerOrderLineModel>();
this.CustomerOrderHeader = new CustomerOrderHeaderModel();
}
public CustomerOrderHeaderModel CustomerOrderHeader
{ get; set; }
public List<CustomerOrderLineModel> CustomerOrderLines
{ get; set; }
}
The Controller:
[HttpPost]
public JsonResult SaveOrUpdateOrderLines( CustomerOrderModel customerOrderModel)
{
if (customerOrderModel.CustomerOrderHeader.OrderNumber == 0)
{
customerOrderModel.CustomerOrderHeader.OrderNumber = this.CustomerOrderService.CreateOrUpdate(customerOrderHeader, false);
}
return
Json(
new
{
OrderNumber = customerOrderModel.CustomerOrderHeader.OrderNumber,
CustomerOrderLines = new
{
CustomerOrderLine = (
from ordeline in custOrderHeader.CustomerOrderLines
select new
{
Id = ordeline.Id,
LineNumber = ordeline.LineNumber,
LineStatus = ordeline.LineStatus
}
).ToArray()
}
},
JsonRequestBehavior.AllowGet
);
}
The CustomerOrder Service:
public class CustomerOrderService : ServiceBase, ICustomerOrderService
{
public long CreateOrUpdate(CustomerOrderHeader customerOrderHeader, bool updateCustomerOrderHeader)
{
using (var session = this.SessionManager.OpenSession())
{
var transaction = session.BeginTransaction();
try
{
//TODO: CHECKS IF PARTNER EXISTS
bool existsPartner = false;
long number = 0;
if (this.PartnerService.FindByName(customerOrderHeader.Partner.Name) != null)
{
existsPartner = true;
}
//CREATE PARTNER AND PARTNER ADDRESSES
if (!existsPartner)
{
this.PartnerService.Create(customerOrderHeader.Partner as Partner);
}
if (!updateCustomerOrderHeader)
{
number = this.CustomerOrderHeaderService.Create(customerOrderHeader, true);
}
else
{
this.CustomerOrderHeaderService.Create(customerOrderHeader, false);
}
transaction.Commit();
return number;
}
catch (Exception ex)
{
transaction.Rollback();
throw ex;
}
finally
{
session.Close();
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomerOrderService"/> class.
/// </summary>
/// <param name="sessionManager">
/// The session manager.
/// </param>
public CustomerOrderService(ISessionManager sessionManager)
{
this.SessionManager = sessionManager;
}
}
The CustomerOrderHeaderService:
public long Create(CustomerOrderHeader customerOrderHeader, bool firstTime)
{
using (var session = this.SessionManager.OpenSession())
{
// var transaction = session.BeginTransaction();
// try
// {
if (firstTime)
{
foreach (var customerOrderLine in customerOrderHeader.CustomerOrderLines)
{
customerOrderLine.CustomerOrderHeader = customerOrderHeader;
}
customerOrderHeader.OrderNumber = this.NextOrderNumber();
session.Save(customerOrderHeader);
}
else
{
CustomerOrderHeader customerOrderHeaderToBeChanged =
this.FindByOrderNumber(customerOrderHeader.OrderNumber);
customerOrderHeaderToBeChanged.CopyDomainProperties(
customerOrderHeader, new[]
{
"Id",
"Partner",
"DataOwner",
"dataOwner",
"SysCreatedOn",
"SysCreatedBy",
"CustomerOrderLines"
});
customerOrderHeaderToBeChanged = UpdateLines(customerOrderHeader, customerOrderHeaderToBeChanged);
List<ICustomerOrderLine> linestoBeInserted = GetTheLineNumbersToBeInserted(customerOrderHeader);
foreach (var customerOrderLine in linestoBeInserted)
{
customerOrderHeaderToBeChanged.CustomerOrderLines.Add(customerOrderLine);
customerOrderLine.CustomerOrderHeader = customerOrderHeaderToBeChanged;
}
session.Update(customerOrderHeaderToBeChanged);
}
// transaction.Commit();
}
// catch (Exception ex)
// {
// transaction.Rollback();
// throw ex;
// }
// finally
// {
// session.Close();
// }
//}
//throw new System.ArgumentException();
return customerOrderHeader.OrderNumber;
}
public class CustomerOrderHeaderService : ServiceBase, ICustomerOrderHeaderService
{
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="CustomerOrderHeaderService"/> class.
/// </summary>
/// <param name="sessionManager">
/// The session manager.
/// </param>
public CustomerOrderHeaderService(ISessionManager sessionManager)
{
this.SessionManager = sessionManager;
}
#endregion
public long Create(CustomerOrderHeader customerOrderHeader, bool firstTime)
{
using (var session = this.SessionManager.OpenSession())
{
// var transaction = session.BeginTransaction();
// try
// {
if (firstTime)
{
foreach (var customerOrderLine in customerOrderHeader.CustomerOrderLines)
{
customerOrderLine.CustomerOrderHeader = customerOrderHeader;
}
customerOrderHeader.OrderNumber = this.NextOrderNumber();
session.Save(customerOrderHeader);
}
else
{
CustomerOrderHeader customerOrderHeaderToBeChanged =
this.FindByOrderNumber(customerOrderHeader.OrderNumber);
customerOrderHeaderToBeChanged.CopyDomainProperties(
customerOrderHeader, new[]
{
"Id",
"Partner",
"DataOwner",
"dataOwner",
"SysCreatedOn",
"SysCreatedBy",
"CustomerOrderLines"
});
customerOrderHeaderToBeChanged = UpdateLines(customerOrderHeader, customerOrderHeaderToBeChanged);
List<ICustomerOrderLine> linestoBeInserted = GetTheLineNumbersToBeInserted(customerOrderHeader);
foreach (var customerOrderLine in linestoBeInserted)
{
customerOrderHeaderToBeChanged.CustomerOrderLines.Add(customerOrderLine);
customerOrderLine.CustomerOrderHeader = customerOrderHeaderToBeChanged;
}
session.Update(customerOrderHeaderToBeChanged);
}
// transaction.Commit();
}
// catch (Exception ex)
// {
// transaction.Rollback();
// throw ex;
// }
// finally
// {
// session.Close();
// }
//}
//throw new System.ArgumentException();
return customerOrderHeader.OrderNumber;
}
}
Increase your connection pool limits can help make it last longer under this, as long as you don't saturate the server/network with too many.
Open and close database connections as late and early (respectively) as possible. You may want to switch your session open/close from per-request (or whatever you are using) to on-use.
In your case, since it's a button on the interface, you can temporarily disable it (with JavaScript) for a period of time so the user can't keep clicking it faster than they should ever need to.
Regardless, you should rate limit the incoming requests either at the network or IIS level.
Make sure that you aren't sharing objects across multiple NHibernate sessions.
That error could occur if Session #1 loads an object that Session #2 tries to commit.
Related
I am having issues publishing generic messages for derived types and having the handler invoked using MassTransit v2.8.0.
If I publish a message of type HtmlBlockNewMessage, the consumer is never invoked. If I publish a ServiceBusMessage object and change the consumer to Consumes<ServiceBusMessage>.Context, the consumer is invoked.
The code fails for a derived type. It only works for the parent type (ServiceBusMessage).
Types:
[Serializable]
public class ServiceBusMessage
{
}
[Serializable]
public class ServiceBusResponse
{
public int ResultCode { get; set; }
}
// Request
[Serializable]
public class ContentItemMessage : ServiceBusMessage
{
public string SiteId { get; set; }
public string PageId { get; set; }
public string ContentItemId { get; set; }
}
[Serializable]
public class HtmlBlockNewMessage : ContentItemMessage
{
public string HtmlData { get; set; }
}
// Response
[Serializable]
public class ContentItemMessageResponse : ServiceBusResponse
{
public string Name { get; set; }
public string ItemType { get; set; }
public string ItemHandler { get; set; }
}
[Serializable]
public class HtmlBlockNewMessageResponse : ContentItemMessageResponse
{
public string DataId { get; set; }
}
Consumer:
public class HtmlBlockConsumer : Consumes<HtmlBlockNewMessage>.Context
{
private IHtmlDataService htmlDataService;
public static ILogger Logger { get; set; }
public HtmlBlockConsumer()
: this(null)
{
}
public HtmlBlockConsumer(IHtmlDataService htmlDataService)
{
Logger = Log4NetLogger.GetLogger();
this.htmlDataService = htmlDataService ?? IoC.Container.Resolve<IHtmlDataService>();
}
public void Consume(IConsumeContext<HtmlBlockNewMessage> message)
{
// Do some stuff
message.Respond(new HtmlBlockNewMessageResponse() { ResultCode = 1 } );
}
}
Bus registration from publisher side:
var bus = ServiceBusFactory.New(sbc =>
{
sbc.EnableMessageTracing();
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.SetNetwork("Test");
sbc.UseXmlSerializer();
sbc.UseControlBus();
sbc.ReceiveFrom("msmq://localhost/AuctionCMS.Web.Publisher");
MtServiceBus.ValidateBus(sbc);
});
IoC.Container.RegisterInstance(bus);
Bus registration from consumer side:
var bus = ServiceBusFactory.New(sbc =>
{
sbc.EnableMessageTracing();
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.SetNetwork("Test");
sbc.UseXmlSerializer();
sbc.UseControlBus();
sbc.ReceiveFrom("msmq://localhost/AuctionCMS.Consumer");
sbc.Subscribe(subs =>
{
// These are being manually registered due to some issues getting
// StructureMap to scan my assemblies
subs.Instance(new HtmlBlockConsumer());
subs.Instance(new BrowserConsumer());
subs.Instance(new OfferConsumer());
});
});
IoC.Container.RegisterInstance(bus);
My publish extension:
public static TR Publish<T, TR>(this IServiceBus bus, T message) where T : ServiceBusMessage where TR : ServiceBusResponse
{
TR response = null;
IoC.Container.Resolve<IServiceBus>().PublishRequest(message, callback =>
{
callback.SetTimeout(10.Seconds());
try
{
callback.Handle<TR>(m =>
{
response = m; /
});
}
catch (Exception ex)
{
throw;
}
});
return response;
}
Calling code:
// First I create a message specific to the type of action I am performing
var message = new HtmlBlockNewMessage() { HtmlData = "Hello" };
// Then I call a function which accepts a ContentItemMessage and calls Publish
public void AddContentItem(ContentItemMessage message)
{
// Do some preprocessing
// This call times out
var response = this.auctionCmsServices.Bus.Publish<ContentItemMessage,
ContentItemMessageResponse>(message);
// Do some more processing
}
This is the exception
[RequestTimeoutException: Timeout waiting for response, RequestId: 54910000-307f-20cf-c0c2-08d06b31cf6f]
MassTransit.RequestResponse.RequestImpl`1.Wait() in d:\BuildAgent-03\work\aa063b4295dfc097\src\MassTransit\RequestResponse\RequestImpl.cs:124
MassTransit.RequestResponseExtensions.PublishRequest(IServiceBus bus, TRequest message, Action`1 configureCallback) in d:\BuildAgent-03\work\aa063b4295dfc097\src\MassTransit\RequestResponseExtensions.cs:31
AuctionCMS.Framework.ServiceBus.MtServiceBus.Publish(IServiceBus bus, T message) in c:\Users\rick\Documents\Visual Studio 2012\Projects\AuctionCMS\AuctionCMS.Framework\ServiceBus\MtServiceBus.cs:24
AuctionCMS.Framework.Entity.Page.AddContentItem(ISite site, String zone, Int32 location, ContentItemMessage message) in c:\Users\rick\Documents\Visual Studio 2012\Projects\AuctionCMS\AuctionCMS.Framework\Entity\Page.cs:48
AuctionCMS.Framework.Entity.Site.SetDefaultContent() in c:\Users\rick\Documents\Visual Studio 2012\Projects\AuctionCMS\AuctionCMS.Framework\Entity\Site.cs:117
AuctionCMS.Web.Controllers.AdminSitesController.NewSite(SiteNewModel model, HttpPostedFileBase file) in c:\Users\rick\Documents\Visual Studio 2012\Projects\AuctionCMS\AuctionCMS.Web\Controllers\AdminSitesController.cs:69
lambda_method(Closure , ControllerBase , Object[] ) +179
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +261
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +39
System.Web.Mvc.Async.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41() +34
System.Web.Mvc.Async.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33() +124
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +838059
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +838059
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +838059
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +838059
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +15
System.Web.Mvc.Async.<>c__DisplayClass2a.<BeginInvokeAction>b__20() +33
System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +838644
System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +28
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +65
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +51
System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +42
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +15
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +51
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +606
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288
Edit:
I went with a generic approach to solve this. It's a but ugly from the caller's perspective, but it works.
public TR AddContentItem<T, TR>(T message) where T : ContentItemMessage where TR : ContentItemMessageResponse
{
var response = this.auctionCmsServices.Bus.Publish<T, TR>(message);
return response;
}
The calling code now looks like this:
page.AddContentItem(new HtmlBlockNewMessage() { HtmlData =
"This is some html" });
Eugene's comment is correct. What's happening here is you are publishing a message of type ContentItemMessage. A consumer of HtmlBlockNewMessage will not executed since the message is published as a ContentItemMessage and a ServiceBusMessage. MassTransit message mis-typing is one of a number of things out there on how this works.
Your options:
Change AddContentItem to use a generic, perhaps with a constraint
Used reflection to invoke Publish with the right type information
Restructure how you publish things so this isn't an issue any more
The bottom line is you should always publish as the type you want received. Polymorphism in messaging is tricky.
I have an ASP.NET MVC 3 application using ASP.NET Membership. I get strange error in production environment. One client complains that they always get an error when they try to log in using Chrome. It works with IE. However for another client they get same error when they log in with IE but works in Chrome.
This is the code for Logon
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
if (returnUrl.Contains("CorporaClaim"))
ViewBag.Layout = _corporaLayout;
else
ViewBag.Layout = _layout;
return Redirect(returnUrl);
}
else
{
ViewBag.Layout = _layout;
// redirect user according to user role
if (!string.IsNullOrEmpty(model.UserName))
return RedirectToDentalinkActionBasedOnRole(model.UserName);
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
ViewBag.Layout = _layout;
// If we got this far, something failed, redisplay form
return View(model);
}
In my _Layout.cshtml I make a call to the below method to show the logged in user's name:
#{
string displayName = DentalinkWeb.Utility.LoginUtility.GetDisplayForUser(User);
}
public class LoginUtility
{
public static string GetDisplayForUser(IPrincipal User)
{
string displayName = "";
if (User.Identity.IsAuthenticated)
{
if (User.IsInRole("Practice Admin"))
{
DentalinkEntities db = new DentalinkEntities();
RegisteredPractice prac = (from p in db.RegisteredPractices where p.Email == User.Identity.Name select p).FirstOrDefault();
displayName = prac.DentistFirstName + " " + prac.DentistSurname;
}
else if (User.IsInRole("Dentalink Admin"))
{
displayName = "Dentalink Admin";
}
}
return displayName;
}
}
}
We get this error:
System.NullReferenceException: Object reference not set to an instance of an object.
at DentalinkWeb.Utility.LoginUtility.GetDisplayForUser(IPrincipal User)
at ASP._Page_Views_Shared__Layout_cshtml.Execute() in e:\web\dentalinkco\htdocs\Views\Shared\_Layout.cshtml:line 51
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
at System.Web.WebPages.WebPageBase.<>c__DisplayClass7.<RenderPageCore>b__6(TextWriter writer)
at System.Web.WebPages.HelperResult.WriteTo(TextWriter writer)
at System.Web.WebPages.WebPageBase.Write(HelperResult result)
at System.Web.WebPages.WebPageBase.RenderSurrounding(String partialViewName, Action`1 body)
at System.Web.WebPages.WebPageBase.PopContext()
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
at System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
at System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer)
at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
at System.Web.Mvc.Controller.ExecuteCore()
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__5()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.MvcHandler.<>c__DisplayClasse.<EndProcessRequest>b__d()
at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Please help.
Only thing I can think of will be to add User != null in if statement.
if (User != null && User.Identity.IsAuthenticated)
{
...
RegisteredPractice prac = (from p in db.RegisteredPractices
where p.Email == User.Identity.Name select p).FirstOrDefault();
if(prac != null) // Just to be on safe side.
{
displayName = prac.DentistFirstName + " " + prac.DentistSurname;
}
...
}
Here's a revised code.
public class LoginUtility
{
public static string GetDisplayForUser(IPrincipal User)
{
string displayName = "";
if (User != null)
{
if (User.Identity.IsAuthenticated)
{
if (User.IsInRole("Practice Admin"))
{
DentalinkEntities db = new DentalinkEntities();
RegisteredPractice prac = (from p in db.RegisteredPractices where p.Email == User.Identity.Name select p).FirstOrDefault();
displayName = prac.DentistFirstName + " " + prac.DentistSurname;
}
else if (User.IsInRole("Dentalink Admin"))
{
displayName = "Dentalink Admin";
}
}
}
return displayName;
}
}
I had an MVC 3 controller action defined like this:
public ActionResult _UpdateFilter(int a, string b, List<string> c)
that worked great. There's a new requirement to sometimes also send a Dictionary<int, bool> to the action, depending on user action in the UI.
To populate that dictionary, I add hidden fields to the appropriate form like this:
<input type="hidden" id="id-#(something)-Key" name="sometimesSet[#(idx)].Key" value="#(myIntValue)" />
<input type="hidden" id="id-#(something)-Value" name="sometimesSet[#(idx)].Value" value="#(myBoolValue)" />
and I extended my Controller Action like this:
public ActionResult _UpdateFilter(int a, string b, List<string> c, Dictionary<int, bool> sometimesSet = null)
So far, so good. As long as I populate at least one hidden key/value pair in the form. If I do not, my Action is not invoked and I get the exception:
Value cannot be null.
Parameter name: key
(long stack trace at end of question).
The Question
I gather the route mapping can't figure out that sometimesSet is optional, but I don't know how to configure that properly. How do I do that? I have not edited the route definitions in Global.asax.
The stack trace
at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.ContainsKey(TKey key)
at System.Web.Mvc.ValueProviderUtil.GetKeysFromPrefix(IEnumerable`1 collection, String prefix)
at System.Web.Mvc.DictionaryValueProvider`1.GetKeysFromPrefix(String prefix)
at System.Web.Mvc.ValueProviderCollection.GetKeysFromPrefixFromProvider(IValueProvider provider, String prefix)
at System.Web.Mvc.ValueProviderCollection.<>c__DisplayClass11.<GetKeysFromPrefix>b__c(IValueProvider provider)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
at System.Web.Mvc.ValueProviderCollection.GetKeysFromPrefix(String prefix)
at System.Web.Mvc.DefaultModelBinder.UpdateDictionary(ControllerContext controllerContext, ModelBindingContext bindingContext, Type keyType, Type valueType)
at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.<BeginInvokeAction>b__1e(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state)
at System.Web.Mvc.Controller.<>c__DisplayClass1d.<BeginExecuteCore>b__17(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)
at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__3(AsyncCallback asyncCallback, Object asyncState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<BeginProcessRequest>b__2()
at System.Web.Mvc.SecurityUtil.<>c__DisplayClassb`1.<ProcessInApplicationTrust>b__a()
at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust[TResult](Func`1 func)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
I found the cleanest solution is to implement my own ModelBinder that understands how to populate a Dictionary<K,V> even if no relevant values are found on the wire (it will return an empty Dictionary<K,V>.
Here's the code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace My.ModelBinders
{
/// <summary>
/// Binds to a generic Dictionary using the basic format outlined at
/// http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
/// with the relaxation that the indices can be arbitrary integers (need not start at 0 or be sequential).
/// Returns an empty dictionary of no matching parameters found on the wire rather than throwing
/// an Exception as the current default binder does.
/// </summary>
/// <typeparam name="K">Key type</typeparam>
/// <typeparam name="V">Value type</typeparam>
public class OptionalDictionaryBinder<K,V> : CustomBinderBase, IModelBinder
{
/// <summary>
/// Pull key/value pairs out of request. Modified from
/// https://github.com/loune/MVCStuff/blob/master/Extensions/DefaultDictionaryBinder.cs
/// Files not currently supported.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private IEnumerable<KeyValuePair<string, string>> GetValueProviderData(ControllerContext context)
{
foreach (var fk in context.HttpContext.Request.Form.Keys)
{
yield return new KeyValuePair<string, string>((string)fk, context.HttpContext.Request.Form[(string)fk]);
}
foreach (var rd in context.RouteData.Values)
{
yield return new KeyValuePair<string, string>(rd.Key, (string)rd.Value);
}
foreach (var qp in context.HttpContext.Request.QueryString)
{
yield return new KeyValuePair<string, string>((string)qp, context.HttpContext.Request.QueryString[(string)qp]);
}
}
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}
Dictionary<K, V> dict = new Dictionary<K, V>();
Dictionary<int, K> keys = new Dictionary<int, K>();
Dictionary<int, V> values = new Dictionary<int, V>();
foreach (KeyValuePair<string,string> formParam in GetValueProviderData(controllerContext))
{
if (formParam.Key.StartsWith(bindingContext.ModelName, StringComparison.InvariantCultureIgnoreCase))
{
int startbracket = formParam.Key.IndexOf("[");
if (startbracket != bindingContext.ModelName.Length) throw new Exception("Did not find [ directly after model name");
int endbracket = formParam.Key.IndexOf("]", bindingContext.ModelName.Length + 1);
if (endbracket == -1) throw new Exception("Did not find closing bracket in " + formParam);
int idx;
string idxText = formParam.Key.Substring(bindingContext.ModelName.Length + 1, endbracket - bindingContext.ModelName.Length - 1);
if (!int.TryParse(idxText, out idx))
{
throw new Exception("Could not parse numeric index from " + formParam);
}
if (formParam.Key.EndsWith(".Key", StringComparison.InvariantCultureIgnoreCase))
{
if (keys.ContainsKey(idx))
{
throw new Exception("The index " + idx + " was repeated.");
}
K dictKey;
try
{
dictKey = (K)Convert.ChangeType(formParam.Value, typeof(K));
keys.Add(idx, dictKey);
}
catch (NotSupportedException)
{
throw new Exception("The given key '" + formParam.Key + "' could not be converted to type K");
}
}
else if (formParam.Key.EndsWith(".Value", StringComparison.InvariantCultureIgnoreCase))
{
if (values.ContainsKey(idx))
{
throw new Exception("The index " + idx + " was repeated.");
}
V dictValue;
try
{
dictValue = (V)Convert.ChangeType(formParam.Value, typeof(V));
values.Add(idx, dictValue);
}
catch (NotSupportedException)
{
throw new Exception("The given value '" + formParam.Value + "' could not be converted to type V");
}
}
}
}
if (!keys.Keys.SequenceEqual(values.Keys))
{
throw new Exception("Keys and values do not match.");
}
foreach (KeyValuePair<int, K> kvp in keys)
{
dict.Add(kvp.Value, values[kvp.Key]);
}
return dict;
}
}
}
Usage:
public ActionResult _UpdateFilter(int a, string b, List<string> c,
[ModelBinder(typeof(OptionalDictionaryBinder<int, bool>))]
Dictionary<int, bool> sometimesSet)
I have checked out the documentation for restsharp and also googled for a solution, but unable to find a solution for this problem:
I have an asp.net mvc3 app and need to connect to quizlet.com to access public flashcards. I have added a reference to restsharp and created the following classes:
public class QuizletObject
{
public string response_type { get; set; }
public int total_results { get; set; }
public int page { get; set; }
public int total_pages { get; set; }
public int sets_with_images { get; set; }
public Set[] sets { get; set; }
}
public class Set
{
public int id { get; set; }
public string title { get; set; }
public string url { get; set; }
public string creator { get; set; }
public int created { get; set; }
public int term_count { get; set; }
public bool has_images { get; set; }
public int last_modified { get; set; }
}
I have a class and method that calls the web service like this:
public class Quizlet
{
private string _baseUrl = "http://api.quizlet.com/1.0/sets";
private const string DevKey = "my dev key";
public QuizletObject GetQuizletSets()
{
_baseUrl = _baseUrl + "?dev_key=" + DevKey + "&q=" + "geography";
RestClient client = new RestClient();
client.BaseUrl = _baseUrl;
RestRequest request = new RestRequest(Method.GET);
request.RequestFormat = DataFormat.Json;
RestResponse<QuizletObject> response = client.Execute<QuizletObject>(request);
return response.Data;
}
}
When I try to access the method using the below code in the view, I get a NullReference exception (Object reference not set to an instance of an object).
#{
QuizletObject quizletObject = quizlet.GetQuizletSets();
}
#foreach (Set quizletSet in quizletObject.sets)
{
#quizletSet.id.ToString()
<br />
#quizletSet.title
<br />
#quizletSet.creator
<br />
#quizletSet.created
<br />
#Html.Encode(quizletSet.url)
<br />
<br />
}
If the same method is executed as below, it returns the complete json object without any problem
RestResponse response = client.Execute(request);
Stack trace:
[NullReferenceException: Object reference not set to an instance of an object.]
ASP._Page_Views_Home_Index_cshtml.Execute() in c:\Users\Girish\Documents\Visual Studio 2010\Projects\RestSharpPoC\RestSharpPoC\Views\Home\Index.cshtml:18
System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +207
System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +81
System.Web.WebPages.StartPage.RunPage() +19
System.Web.WebPages.StartPage.ExecutePageHierarchy() +65
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +76
System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +220
System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +115
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +303
System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13
System.Web.Mvc.<>c__DisplayClass1c.<InvokeActionResultWithFilters>b__19() +23
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +260
System.Web.Mvc.<>c__DisplayClass1e.<InvokeActionResultWithFilters>b__1b() +19
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +177
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
System.Web.Mvc.Controller.ExecuteCore() +116
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8963149
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
I have seen the same issue. I haven't figured it out in RestSharp, but what I do to work around it is get the json text back then deserialize it myself.
For your code maybe something like this would work:
response = client.Execute(request);
var quizletObject = Newtonsoft.Json.JsonConvert.DeserializeObject<QuizletObject>(response.Content);
As an aside, it looks like you have a problem in your GetQuizletSets() call - each time you call it, it appends _baseUrl to itself so it will continually get longer and longer.
You should keep the base url the same and use request.Resource to specify the parameters, this case it would be:
request.Resource = "?dev_key=" + DevKey + "&q=" + "geography"
If there are multiple available calls you want to make besides "sets" I might set it up like this:
private string _baseUrl = "http://api.quizlet.com/1.0";
...
request.Resource = "sets?dev_key=" + DevKey + "&q=" + "geography";
Edit: Just thought of something else - try using List instead of Set[], maybe RestSharp will have an easier time with that.
Im trying to implement the RequireLocalHostActionFilter that Phil Haack did in one of his shows.
Its the ActionFilter thats checks if a call to a giving method is from the local host, and is registred in the global filters.
But my filter is not working, and I cant get my head around it.
So please, if someone has some free time to take a look.
My ActionFilter:
public class RequireLocalHostActionFilter : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return !httpContext.Request.IsLocal; // I need to test on the local host, so I reverse the logic.
}
}
My FilterProvider
public class ConditionalFilterProvider : IFilterProvider
{
public readonly IEnumerable<Func<ControllerContext, ActionDescriptor, object>> _conditions;
public ConditionalFilterProvider(IEnumerable<Func<ControllerContext, ActionDescriptor, object>> conditions)
{
this._conditions = conditions;
}
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
IEnumerable<Filter> result = from condition in _conditions
select condition(controllerContext, actionDescriptor)
into filter
where filter != null
select new Filter(filter, FilterScope.Global, null);
return result;
}
}
In globals.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var conditions = new Func<ControllerContext, ActionDescriptor, object>[]
{
(c, a) =>
a.ControllerDescriptor.ControllerName.Equals("Online", StringComparison.OrdinalIgnoreCase)
? null : new RequireLocalHostActionFilter()
};
filters.Add(new ConditionalFilterProvider(conditions));
filters.Add(new HandleErrorAttribute());
}
I can see the action filter is added into the collection of filters.
And last my OnlineController, a simpel control, that I want the global filter to kick in.
public class OnlineController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC! online";
return View();
}
// thx for taking your time to read this post.
// dennis
My if statement needed to be reversed.
var conditions = new Func < ControllerContext,
ActionDescriptor, object > [] {
(c, a) =>
a.ControllerDescriptor.ControllerName.Equals("Online",
StringComparison.OrdinalIgnoreCase) * * ? new RequireLocalHostActionFilterAttribute() : null * *
};
And I forgot to added the filter to the top of the controller.
// dennis