DisplayModeProvider.Instance.Modes.Add Called unconditionally - c#

Our current project is utilizing the following code snippet found online to direct users to a mobile view as required:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Mobile")
{
ContextCondition = (context => RequirementsHelper.BrowserIsMobile(context.GetOverriddenUserAgent()))
});
It allows us to easily direct users to index.cshtml or index.mobile.cshtml depending on their user agent string. So far so good.
Extending off this idea, I was tempted to implement a localization engine based on DisplayModeProvider (as different versions of our sites are to look significantly different, but function almost identically.)
So my initial quick test was to create the following method:
protected void DisplayNZSkin()
{
System.Web.WebPages.DisplayModeProvider.Instance.Modes.Add(new System.Web.WebPages.DefaultDisplayMode("NZ")
{
ContextCondition = (context => true)
});
}
Which I'll be able to call when I determine that the NZ skin is to show (which unfortunately relies on some database calls). The idea was that when this is called, it forces the rendering of index.nz.cshtml.
It was a bad idea. The mere existence of this method in my controller, uncalled, made all pages render their .nz versions.
Stepping through, shows that the code is always executed. The Function (context => true) is invoked in every page call.
What's going on there?

I had almost the same Idea, so I'll put my version and let's see if it solves your problem.
I started with this article
http://www.codeproject.com/Articles/576315/LocalizeplusyourplusMVCplusappplusbasedplusonplusa
but changed this to use this because the filter was after the modelbinding.
public class ExtendedControllerFactory:DefaultControllerFactory
{
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(GetCultureFromURLOrAnythingElse());
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(GetCultureFromURLOrAnythingElse());
return base.GetControllerInstance(requestContext, controllerType);
}
and this way the global.asax should have this
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new ExtendedControllerFactory());
System.Web.WebPages.DisplayModeProvider.Instance.Modes.Insert(0, new System.Web.WebPages.DefaultDisplayMode("EN")
{
ContextCondition = context => Thread.CurrentThread.CurrentCulture.Name.Equals("en-US", StringComparison.CurrentCultureIgnoreCase)
});
System.Web.WebPages.DisplayModeProvider.Instance.Modes.Insert(0, new System.Web.WebPages.DefaultDisplayMode("BR")
{
ContextCondition = context => Thread.CurrentThread.CurrentCulture.Name.Equals("pt-BR", StringComparison.CurrentCultureIgnoreCase)
});
I think this should do what you want, but I never tried this in production.

Related

Is there a way of writing a FindElement call by passing in a string value for type

I am currently writing a Selenium UI test for our new transactional website however whilst setting up the solution I am trying to find a more efficient way of creating a method to find my element without duplicate of code.
Ideally I want to create something akin to this:
public void SearchForElement(IWebDriver driver, string elementType, string elementReference)
{
driver.FindElement(By.Id(elementReference));
}
Where 'Id' is the variable value 'elementType'
In previous solutions I have done the following:
if (elementType == "Id")
{
returnElement = driver.FindElement(By.Id(elementReference));
}
else if (elementType == "Name")
{
returnElement = driver.FindElement(By.Name(elementReference));
}
else if (elementType == "CssSelector")
{
returnElement = driver.FindElement(By.CssSelector(elementReference));
}
else if (elementType == "XPath")
{
returnElement = driver.FindElement(By.XPath(elementReference));
}
else if (elementType == "ClassName")
{
returnElement = driver.FindElement(By.ClassName(elementReference));
}
However as you can see the latter is in effect the same line of code that is only differentiated by the type.
Any help/advice would be much appreciated.
As the UI tests are being written in parallel to the development of our transactional site (Not ideal I know) I find it easier to maintain an array of the elements that I am using
There is nothing wrong with writing automated tests in parallel to development. In fact, this is the ideal way to do it. You actually have a much bigger problem than the question you ask. Your problem can be addressed with a different architecture for your tests. Encapsulating automation behavior in a Selenium Page Object Model allows you to partially create classes that other testers can use, but defer things like defining locators until you have a user interface to deal with.
As an example, imagine you are automating the UI for a todo list app. The UI isn't built yet, but that doesn't stop you from writing a test:
todoList = new TodoListPageModel(driver);
todoList.Add("Pay the electric bill");
CollectionAssert.Contains(todoList.GetItems().ToList(), "Pay the electric bill");
Your page model would look like:
public class TodoListPageModel
{
private readonly IWebDriver driver;
private IWebElement AddButton => throw new NotImplementedException();
private IWebElement TodoTextField => throw new NotImplementedException();
private IEnumerable<IWebElement> Items => throw new NotImplementedException();
public TodoListPageModel(IWebDriver driver)
{
this.driver = driver;
}
public void Add(string todo)
{
TodoTextField.SendKeys(todo);
AddButton.Click();
}
public IEnumerable<string> GetItems()
{
return Items.Select(item => item.Text.Trim());
}
}
Properties that refer to elements or collections of elements can throw a NotImplementedException until you have a user interface. Then you can implement those properties using the correct locator, and run the tests.
Depending on who gets done first, you could inform the developer that the tests are written, and they just need to fill in the locators. After that the developer has tests to run during development, which is pretty nice.

Seeding initial user on Identity module without double-seeding

I'm trying to use the ABP's identity module and have a seed for my first (admin) user.
In the identity module seed contributor's source code I see this:
public Task SeedAsync(DataSeedContext context)
{
return _identityDataSeeder.SeedAsync(
context["AdminEmail"] as string ?? "admin#abp.io",
context["AdminPassword"] as string ?? "1q2w3E*",
context.TenantId
);
}
So in my migrator module I added this:
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
using (var scope = context.ServiceProvider.CreateScope())
{
var dataSeeder = scope.ServiceProvider.GetRequiredService<IDataSeeder>();
var dsCtx = new DataSeedContext
{
["AdminEmail"] = "my#admin-email",
["AdminPassword"] = "my-admin-password"
};
AsyncHelper.RunSync(() => dataSeeder.SeedAsync(dsCtx));
}
base.OnApplicationInitialization(context);
}
This works... however there's probably another module creating a data seeder (more likely the one that actually gets executed on the migrator, but I can't really find it), so all my contributors (and probably the module contributors) get executed twice (that's to be expected, I guess).
Is there any way I can change the seeding context without actually running an IDataSeeder? and if this can't be done... is there a way I can "unregister" all IDataSeeders previous to mine so only mine gets executed?
The solution to this specific question (although I was hoping to find a more "general" solution), was to change the actual contributor. On your domain module (or wherever you see fit: your migrator or whatever), just do:
// Remove the contributor for the module
context.Services.RemoveAll(t => t.ImplementationType == typeof(IdentityDataSeedContributor));
// Add my custom constributor
context.Services.AddTransient<IDataSeedContributor, MyCustomConstributor>();
Where the implementation of the contributor is simply a copy of the default:
public class MyCustomConstributor : IDataSeedContributor
{
private readonly IIdentityDataSeeder _identityDataSeeder;
public IdentityDataSeedContributor(IIdentityDataSeeder identityDataSeeder)
{
_identityDataSeeder = identityDataSeeder;
}
public Task SeedAsync(DataSeedContext context)
{
return _identityDataSeeder.SeedAsync(
context["AdminEmail"] as string ?? "my#admin-email",
context["AdminPassword"] as string ?? "my-admin-password",
context.TenantId
);
}
}
Notice that you still get the username admin here... if you want to change it, you can just replace also the IIdentityDataSeeder implementation (using the same method, or the easier Services.Replace, which you can use since there should only be one implementation of IIdentityDataSeeder) and copy your own from the default one, changing the searched username.
For now, replacing the services on your module seems the way to go. Maybe the possibility to directly intercept the initialization stages of other modules might be there on future versions, but I haven't seen how for now.

What's the best way to keep access to the ApplicationUserManager after the controller has been disposed?

Ran into an interesting problem yesterday. Only my mvc website with Identity 1 I used an IPrincipal extension method to do custom authentication, which worked fine across all the razor views where it was needed. Now with Identity 2, I need to get access in the extension method to the ApplicationUserManager, so I've been forced to pass the HttpContext.Current through from all the views.
This works fine, apart from inside razor #section sections - following the call stack, it appears that these don't get rendered until after the controller has been disposed, which disposes my database and user managers etc.
What would be the best way around this limitation? I want to keep using sections, as they let me easily inject javascript down the very bottom of the html, but I also want to keep using razor extensions inside these sections as they are a very nice, easy way to control what gets outputted to the page.
At the moment I'm being forced to create a new ApplicationUserManager and MyDbContext, stuff them in the ViewBag and then pass them along to the extension, which isn't ideal.
Example of my extension method:
public static bool HasPermission(this IPrincipal source, ApplicationUserManager um, MyDbContext db, string controller, string method) {
if (!source.Identity.IsAuthenticated) {
return false;
}
var roles = um.GetRoles(source.Identity.GetUserId());
if (roles.Count == 0) {
return false;
}
// Admins have global access
string role = roles[0];
if (role.Equals("Admin")) {
return true;
}
// This is fine being un-domained, permissions are global
PermissionMatrix permission = db.PermissionMatrices.FirstOrDefault(x => x.Controller == controller && x.Action == method && x.RoleName == role);
return permission != null;
}
Example of where it's used:
#if (User.HasPermission((ApplicationUserManager)ViewBag.UserManager, (MyDbContext)ViewBag.Database, "Event", "Create")) {
#Html.ActionLink("Create New", "Create")
}
Editing to make the problem itself more clear:
This was the extension method before I put my hack in -
public static bool HasPermission(this IPrincipal source, HttpContext context, string controller, string method) {
var userManager = context.GetOwinContext().Get<ApplicationUserManager>();
var myDb = context.GetOwinContext().Get<MyDbContext>();
Like I kind of said above, this works (passing the HttpContext.Current through from a .cshtml page), UNLESS the call is inside a razor #section, then it fails, because these run AFTER the controller has been disposed, which disposes the stuff inside the OwinContext.
The code above is the hacky way I've got it working at the moment; in my Controllers I have to create an extra MyDbContext and an extra ApplicationUserManager.

Response.Redirect issue with Asp.net async

I'm new to asp.net 4.5 async and am running into the following with calling response.redirect within an async method. The issue is that the response just "hangs" Has anyone else experienced similar issues with attempting an redirect with async? This code will work in a brand new project, but, does not work with a new page in our existing code. I made sure to gut out everything I could out of our web.config and removed our master page. Hitting a brick wall...any ideas? Thanks!
protected void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(PageLoadAsync));
}
private async Task PageLoadAsync()
{
var data = await GetData();
if (data == HttpStatusCode.OK)
Response.Redirect("http://www.google.com");
}
private async Task<HttpStatusCode> GetData()
{
using (var client = new HttpClient())
{
var response = await client.GetAsync("https://www.google.com");
return response.StatusCode;
}
}
This code will work in a brand new project, but, does not work with a new page in our existing code.
I assume your existing site has already been upgraded to .NET 4.5.
The first thing to check is that httpRuntime.targetFramework is set to 4.5. This is not set by default when you upgrade.
Edit from comments:
Another thing to check (just in case) is that Page.Async is set to true.
In this case, the solution was to call Response.Redirect("http://www.google.com", false), which explicitly passes false for the endResponse parameter. The default value of true is only for backwards-compatibility reasons as described here.
The hack I used is:
I used a static dictionary as var d= new Dictionary<string, bool>(); in the class where my API calling method is written.
I put the code line client.timeout = new System.TimeSpan(0,0,60); for API sending the request.
When API is timed out, it throws the TaskTimeoutException, in the TaskTimeoutExceptioncatch block write code as d.Add("timeout", true);
Now, I created the custom action filter and applied the following code:
public class MyCustomActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if(MyApiClass.d.ContainsKey("timeout") && d["timeout"])
{
throw new Exception();
}
}
}
I applied the [MyCustomActionFilter ] on the action.
When action is executed and enter the custom filter it throws Exception by checking the dictionary entry.
If timeout would have occurred then dictionary entry will be true, so, on the basis of that, we check the entry and throws the exception. Now, we have Application_Error() in Global.asax.cs that catches the exception.
In the Application_Error() we have written the code for redirect to the required page.
NOTE: In step 4 you can create your custom exception to provide more precise detail for logging.

How should NHibernate session be handled in Nancy for session-per-request?

Really, my my question is in the title... How should NHibernate session be handled in Nancy for session-per-request? If you've got a good answer for that in and of itself, go for it... if you need more background, here it is:
I am accustomed to using an actionFilter in ASP.NET MVC to open and close a session in the NHibernate context at the beginning and end of a web request. That way, each database operation within the context of a request uses the same session.
I thought I had the same kind of thing set up in my new project that uses Nancy, but every time a session is needed, a new one is generated. Here's how I'm handling the opening and closing of session in my bootstrapper (inheriting from StructureMapBootstrapper):
protected override void RequestStartup(IContainer container, IPipelines pipelines, NancyContext context)
{
var sessionContainer = container.GetInstance<ISessionContainer>();
pipelines.BeforeRequest.AddItemToStartOfPipeline(x =>
{
sessionContainer.OpenSession();
return x.Response;
});
pipelines.AfterRequest.AddItemToEndOfPipeline(x => sessionContainer.CloseSession());
}
My ISessionContainer is based off of something similar to this site. My implementation of ISessionContainer uses NHibernate's session context to get the "current session".
Right now, when I try this in my Nancy project, a new session is returned each time the ISessionContainer.Session property is requested. I thought it was because cookie-based sessions are not enabled by default in Nancy, so I added this to my bootstrapper:
protected override void ApplicationStartup(IContainer container, IPipelines pipelines)
{
CookieBasedSessions.Enable(pipelines);
}
No dice. I'm still given a new session every time ask for one.
But, really, I don't want to diagnose my problem. I'd rather hear what is the standard way to handle NHibernate session management in Nancy.
In my Nancy port of the RestBucks sample I use NHibernate in a seesion per request fashion.
In the bootstrapper from that sample I have the following setup of NHibernate:
protected override void ApplicationStartup(IWindsorContainer container,
Nancy.Bootstrapper.IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
pipelines.BeforeRequest += ctx => CreateSession(container);
pipelines.AfterRequest += ctx => CommitSession(container);
pipelines.OnError += (ctx, ex) => RollbackSession(container);
// Other startup stuff
}
private Response CreateSession(IWindsorContainer container)
{
var sessionFactory = container.Resolve<ISessionFactory>();
var requestSession = sessionFactory.OpenSession();
CurrentSessionContext.Bind(requestSession);
requestSession.BeginTransaction();
return null;
}
private AfterPipeline CommitSession(IWindsorContainer container)
{
var sessionFactory = container.Resolve<ISessionFactory>();
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Commit();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
private Response RollbackSession(IWindsorContainer container)
{
var sessionFactory = container.Resolve<ISessionFactory>();
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Rollback();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
Exactly how you want to setup your NHibernate session will likely differ.
DinnerParty is a port of NerdDinner to Nancy and RavenDB and was recently reviewed by Ayende here http://ayende.com/blog/156609/reviewing-dinner-party-ndash-nerd-dinner-ported-to-ravendb-on-ravenhq?key=0c283ada-e5e8-4b7c-b76b-e9d27bfc0bf9
I believe it uses per-request sessions, because I recall pointing out how a custom module builder could be used. Have a look at the bootstrapper and the RavenAwareModuleBuilder

Categories