Can SolrNet client run at a different machine from solr server? - solrnet

I set up a solr server running in Tomcat in machine 192.168.0.113(Centos 5.5).
And I deploy a website in matchine 192.168.0.114(Windows server 2003).
I use solrnet in matchine 192.168.0.114.
The full code like bellow(which have been edited thanks to #Paige Cook):
using System;
using System.Collections.Generic;
using System.Text;
using SolrNet;
using NUnit.Framework;
using SolrNet.Attributes;
using SolrNet.Commands.Parameters;
using Microsoft.Practices.ServiceLocation;
namespace MySolrNet
{
public class Video
{
private string videoid;
[SolrField("videoid")]
public string Videoid
{
get { return videoid; }
set { videoid = value; }
}
private string videoname;
[SolrField("videoname")]
public string Videoname
{
get { return videoname; }
set { videoname = value; }
}
private string videoorigin;
[SolrField("videoorigin")]
public string Videoorigin
{
get { return videoorigin; }
set { videoorigin = value; }
}
public Video(string id, string name, string origin)
{
this.Videoid = id;
this.Videoname = name;
this.Videoorigin = origin;
}
public Video()
{
}
public void FixtureSetup()
{
Startup.Init<Video>("http://192.168.0.113:8070/solr");
}
public void Add()
{
Video p = new Video("1", "test video", "Solr Test");
ISolrOperations<Video> solr = ServiceLocator.Current.GetInstance<ISolrOperations<Video>>();
solr.Add(p);
solr.Commit();
}
}
[TestFixture]
public class VideoTests
{
[TestFixtureSetUp]
public void FixtureSetup()
{
Startup.Init<Video>("http://192.168.0.113:8070/solr");
}
[Test]
public void Add()
{
Video p = new Video("1", "test video", "Solr Test");
ISolrOperations<Video> solr = ServiceLocator.Current.GetInstance<ISolrOperations<Video>>();
solr.Add(p);
solr.Commit();
}
[Test]
public void Query()
{
ISolrOperations<Video> solr = ServiceLocator.Current.GetInstance<ISolrOperations<Video>>();
SolrQueryResults<Video> results = solr.Query(new SolrQueryByField("videoid", "33013"));
Assert.AreEqual(1, results.Count);
Console.WriteLine(results[0].Videoname);
}
}
}
However,both Add and Query test fail.
It complains: TestFixture failed: SetUp : System.IO.FileLoadException : Could not load file or assembly“SolrNet, Version=0.4.0.2002, Culture=neutral, PublicKeyToken=bc21753e8aa334cb” Or one of its dependencies.
But I have add reference to Microsoft.Practices.ServiceLocation.dll and SolrNet.dll in my projoect,is there any other dll files I just miss?
By the way,,I can access my solr sever in browser with this url:http://192.168.0.113:8070/solr.
Can anyone tell me:
Can I run solrnet and solr in different machines?
How to do it.
Thanks a lot!

I don't understand why you're using two different URLs when you Init Solr. Try changing the URL in Paige's application to the one you posted in your original question: http://192.168.0.113:8070/solr

Thanks for posting the code. The first thing I see is that you are using the test class as the class type to pass data to Solr. Split those out, that might be causing some issues. I would suggest the following:
public class Video
{
private string videoid;
[SolrField("videoid")]
public string Videoid
{
get { return videoid; }
set { videoid = value; }
}
private string videoname;
[SolrField("videoname")]
public string Videoname
{
get { return videoname; }
set { videoname = value; }
}
private string videoorigin;
[SolrField("videoorigin")]
public string Videoorigin
{
get { return videoorigin; }
set { videoorigin = value; }
}
}
[TestFixture]
public class VideoTests
{
[TestFixtureSetUp]
public void FixtureSetup()
{
Startup.Init<Video>("http://192.168.0.113/solr");
}
[Test]
public void Add() {
Video p = new Video("1","test video","Solr Test");
ISolrOperations<Video> solr = ServiceLocator.Current.GetInstance<ISolrOperations<Video>>();
solr.Add(p);
solr.Commit();
}
[Test]
public void Query()
{
ISolrOperations<Video> solr = ServiceLocator.Current.GetInstance<ISolrOperations<Video>>();
SolrQueryResults<Video> results = solr.Query(new SolrQueryByField("videoid", "33013"));
Assert.AreEqual(1, results.Count);
Console.WriteLine(results[0].Videoname);
}
}
Update:
Try this in a console application and see if it works...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SolrNet;
using Microsoft.Practices.ServiceLocation;
namespace Test1
{
class Program
{
static void Main(string[] args)
{
Startup.Init<Video>("http://192.168.0.113:8070/solr");
var solr = ServiceLocator.Current.GetInstance<ISolrOperations<Video>>();
var video = new Video("1", "test", "test");
solr.Add(video);
solr.Commit();
var results = solr.Query(SolrQuery.All);
Console.WriteLine("{0} - {1} - {2}",
results[0].Videoid, results[0].Videoname, results[0].Videoorigin);
}
}
public class Video
{
public Video(string id, string name, string origin)
{
Videoid = id;
Videoname = name;
Videoorigin = origin;
}
public string Videoid { get; set;
public string Videoname { get; set; }
public string Videoorigin { get; set; }
}
}

Can you check your project setup as suggested by Paige?
Do you have SolrNet source code added as project reference in your solution?
If you are using the dll, can you paste your .csproj file contents? If it is urgent, download the source code and add as project reference in your solution until you figure out the issue.

Related

quiz template in unity issue

i've done this unity project following a youtube tutorial.
but i is not working can you figure out the problem??
ok so the first code is
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class AnswerScript : MonoBehaviour
{
public bool isCorrect = false;
public QuizManager quizManager;
public Color startc;
private void Start()
{
startc = GetComponent<Image>().color;
}
public void Answer()
{
if (isCorrect)
{
GetComponent<Image>().color = Color.green;
Debug.Log("Correct Answer");
quizManager.correct();
}
else
{
GetComponent<Image>().color = Color.red;
Debug.Log("Wrong Answer");
quizManager.wrong();
}
}
}
and the second is this one it is a little script
using UnityEngine;
using UnityEngine.UI;
[System.Serializable]
public class QuestionAndANWSER
{
public Image[] Question;
public string[] Answers;
public int CorrectAnswer;
}
the third is this one the biggest script:)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class QuizManager : MonoBehaviour
{
public List<QuestionAndANWSER> Qna;
public GameObject[] options;
public int currentQuestion;
public GameObject QuizPanel;
public GameObject GOPanel;
public Text QuestionTxt;
public Text ScoreTxt;
int totalQuestions = 0;
public int score;
private void Start()
{
totalQuestions = Qna.Count;
GOPanel.SetActive(false);
generateQuestion();
}
public void retry()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
public void GameOver()
{
QuizPanel.SetActive(false);
GOPanel.SetActive(true);
ScoreTxt.text = score +"/"+ totalQuestions;
}
public void correct()
{
score += 1;
Qna.RemoveAt(currentQuestion);
StartCoroutine(WaitforNext());
}
public void wrong()
{
Qna.RemoveAt(currentQuestion);
StartCoroutine(WaitforNext());
}
IEnumerator WaitforNext()
{
yield return new WaitForSeconds(1);
generateQuestion();
}
void SetAnswer()
{
for (int i= 0; i < options.Length; i++)
{
options[i].GetComponent<Image>().color = options[i].GetComponent<AnswerScript>().startc;
options[i].GetComponent<AnswerScript>().isCorrect = false;
options[i].transform.GetChild(0).GetComponent<Text>().text = Qna[currentQuestion].Answers[i];
if(Qna[currentQuestion].CorrectAnswer == i + 1)
{
options[i].GetComponent<AnswerScript>().isCorrect = true;
}
}
}
void generateQuestion()
{
if(Qna.Count > 0)
{
currentQuestion = Random.Range(0, Qna.Count);
QuestionTxt.text = Qna[currentQuestion].Question;
SetAnswer();
}
else
{
Debug.Log("out of questions");
GameOver();
}
}
}
it is a quiz template but i doesn't work. does anyone know how to fix that?
i am a begginer in learning c#
When you define a question and answer:
public class QuestionAndANWSER
{
public Image[] Question;
public string[] Answers;
public int CorrectAnswer;
}
you are storing questions as IMAGES. Later, in generateQuestion(), you're trying to use the questions as TEXT:
void generateQuestion()
{
// ...
QuestionTxt.text = Qna[currentQuestion].Question;
// ...
}
You have to be consistent in how you're handling the questions. You either need to change the QuizManager to use an image for the QuestionTxt or you need to change QuestionAndANSWER to use text for the Question.

Xamarin MVVM passing data to other view

I want to pass the data to another view page. So far I can get the data I need to pass. My problem is how do I pass the data in MVVM. I used Application.Current.MainPage.Navigation.PushAsync(new DatabaseSyncPage(), true); When I add contactId inside DatabaseSyncPage() an error occurs. "The error is 'DatabaseSyncPage' does not contain a constructor that takes 1 arguments"
My code:
LoginPageViewModel.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Input;
using TBSMobileApplication.Data;
using TBSMobileApplication.View;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace TBSMobileApplication.ViewModel
{
public class LoginPageViewModel : INotifyPropertyChanged
{
void OnProperyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
public string username;
public string password;
public string Username
{
get { return username; }
set
{
username = value;
OnProperyChanged(nameof(Username));
}
}
public string Password
{
get { return password; }
set
{
password = value;
OnProperyChanged(nameof(Password));
}
}
public class LoggedInUser
{
public string ContactID { get; set; }
}
public ICommand LoginCommand { get; set; }
public LoginPageViewModel()
{
LoginCommand = new Command(OnLogin);
}
public void OnLogin()
{
if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
{
MessagingCenter.Send(this, "Login Alert", Username);
}
else
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
var link = "http://192.168.1.25:7777/TBS/test.php?User=" + Username + "&Password=" + Password;
var request = HttpWebRequest.Create(string.Format(#link));
request.ContentType = "application/json";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
{
Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
}
else
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if (content.Equals("[]") || string.IsNullOrWhiteSpace(content) || string.IsNullOrEmpty(content))
{
MessagingCenter.Send(this, "Http", Username);
}
else
{
var result = JsonConvert.DeserializeObject<List<LoggedInUser>>(content);
var contactId = result[0].ContactID;
Application.Current.MainPage.Navigation.PushAsync(new DatabaseSyncPage { myId = contactId }, true);
}
}
}
}
else
{
MessagingCenter.Send(this, "Not Connected", Username);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
DatabaseSyncPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TBSMobileApplication.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DatabaseSyncPage : ContentPage
{
public int myId { get; set; }
public DatabaseSyncPage ()
{
InitializeComponent ();
DisplayAlert("Message", Convert.ToString(myId), "ok");
}
}
}
If you want to send the int. First declare that in your DatabaseSyncPage
Like below
public partial class DatabaseSyncPage : ContentPage
{
public DatabaseSyncPage( int Id)
{
}
}
& when you are pushing your page in your code else block do like this
if (content.Equals("[]") || string.IsNullOrWhiteSpace(content) || string.IsNullOrEmpty(content))
{
MessagingCenter.Send(this, "Http", Username);
}
else
{
var result = JsonConvert.DeserializeObject<List<LoggedInUser>>(content);
var contactId = result[0].ContactID;
Application.Current.MainPage.Navigation.PushAsync(new DatabaseSyncPage(contactId), true);
}
I'm assuming that contactID is an int.
Create an additional constructor in your DatabaseSyncPage:
public DatabaseSyncPage (int contactID)
{
// TODO: Do something with your id
}
But this passes the data to the page, not the page model.
Are you using any kind of framework? It would probably be worth looking into that.
You can use xamrin.plugins.settings nuget package.

NHibernate LINQ Add Query Hints

I'm trying to add "OPTION(RECOMPILE)" to the end of some of my NHibernate queries. I found the following post:
http://www.codewrecks.com/blog/index.php/2011/07/23/use-sql-server-query-hints-with-nhibernate-hql-and-icriteria/
This describes how I can add an interceptor to append the SQL. However they are using ICriteria whereas I use LINQ to query my data. Ideally I'd like to be able to say something like:
var query = session.Query<Foo>().OptionRecompile().ToList();
I was wondering if it's possible to add an extension method to IQueryable which will inject some string into the query which I can then detect for in my interceptor. This is similar to the approach used in the article above where they add a comment and detect for that.
For further information. I've dealt with LINQ extensions before and I know you can add extension properties/methods using a HQL generator. But from my understanding this will only allow me to say:
var query = session.Query<Foo>().Where(f => f.Bar.OptionRecompile()).ToList();
This isn't ideal and seems more of a hack. I'd appreciate it if someone can help. Thanks
I ran into this issue recently, too. We came up with a fairly decent/robust solution. The important thing is that it utilizes Rhino.Commons.LocalData to provide an execution scope.
// First part
using System;
using System.Collections;
using System.Web;
using Rhino.Commons.LocalDataImpl;
namespace Rhino.Commons
{
/// <summary>
/// This class is key for handling local data, data that is private
/// to the current context, be it the current thread, the current web
/// request, etc.
/// </summary>
public static class Local
{
static readonly ILocalData current = new LocalData();
static readonly object LocalDataHashtableKey = new object();
private class LocalData : ILocalData
{
[ThreadStatic]
static Hashtable thread_hashtable;
private static Hashtable Local_Hashtable
{
get
{
if (!RunningInWeb)
{
return thread_hashtable ??
(
thread_hashtable = new Hashtable()
);
}
Hashtable web_hashtable = HttpContext.Current.Items[LocalDataHashtableKey] as Hashtable;
if(web_hashtable==null)
{
HttpContext.Current.Items[LocalDataHashtableKey] = web_hashtable = new Hashtable();
}
return web_hashtable;
}
}
public object this[object key]
{
get { return Local_Hashtable[key]; }
set { Local_Hashtable[key] = value; }
}
public void Clear()
{
Local_Hashtable.Clear();
}
}
/// <summary>
/// Gets the current data
/// </summary>
/// <value>The data.</value>
public static ILocalData Data
{
get { return current; }
}
/// <summary>
/// Gets a value indicating whether running in the web context
/// </summary>
/// <value><c>true</c> if [running in web]; otherwise, <c>false</c>.</value>
public static bool RunningInWeb
{
get { return HttpContext.Current != null; }
}
}
}
// Second part
using System;
using Rhino.Commons;
namespace IDL.Core.Util.NHibernate
{
public class NhSqlAppender : IDisposable
{
private static string sql;
private int usages = 1;
public NhSqlAppender()
{
}
public NhSqlAppender(string sqlToAppend)
{
sql = sqlToAppend;
}
public static NhSqlAppender Append(string sqlToAppend)
{
var currentAppender = Current;
if (currentAppender == null)
{
Current = new NhSqlAppender(sqlToAppend);
currentAppender = Current;
}
else
currentAppender.IncrementUsages();
return currentAppender;
}
public static NhSqlAppender Current
{
get { return Local.Data["NhSqlAppender"] as NhSqlAppender; }
protected set { Local.Data["NhSqlAppender"] = value; }
}
public static string Sql
{
get { return (IsValid) ? sql : string.Empty; }
}
public static bool AppendSql
{
get { return IsValid; }
}
public void IncrementUsages()
{
++usages;
}
public void DecrementUsages()
{
--usages;
}
private static bool IsValid
{
get { return (Current != null && !string.IsNullOrWhiteSpace(sql)); }
}
public void Dispose()
{
if (usages <= 1)
Current = null;
else
DecrementUsages();
}
}
}
// Third part
namespace IDL.Core.Util.NHibernate
{
public class NhQueryHint : NhSqlAppender
{
public static NhSqlAppender Recompile()
{
return Append("OPTION(RECOMPILE)");
}
}
}
// Fourth part
using System;
using IDL.Core.Util.NHibernate;
using NHibernate;
namespace IDL.Core.Configuration
{
[Serializable]
public class NhSqlAppenderInterceptor : EmptyInterceptor
{
public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
{
if (NhSqlAppender.AppendSql)
return sql.Insert(sql.Length, (" " + NhSqlAppender.Sql));
return base.OnPrepareStatement(sql);
}
}
}
// Fifth part
// You need to register the interceptor with NHibernate
// cfg = NHibernate.Cfg.Configuration
cfg.SetInterceptor(new NhSqlAppenderInterceptor());
// Finally, usage
using (NhQueryHint.Recompile())
var results = IQueryable<T>.ToList();
You'll have to modify to fit your environment. Hope it helps!
The accepted answer above by #jvukovich helped my out a lot. I built upon his answer by encapsulating the usage of the Recompile hint in an extension method as shown below.
// Extension Method
public static class NhQueryHintExtensions
{
public static TReturn Recompile<T, TReturn>(this IQueryable<T> queryable, Func<IQueryable<T>, TReturn> toFunction)
{
using (NhQueryHint.Recompile())
{
return toFunction(queryable);
}
}
}
// Usage
var results = IQueryable<T>.Recompile(x => x.ToList());

c# - selenium Nunit testing - how to do validation check for signup form

Hi I am trying to test this online signup form. I have created a test but I would like to do a validation check on each field,
e.g.
For Name field
driver.FindElement(By.Id("Name")).SendKeys("Clayton")
I would like to try different text/number/characters and see what goes through and what fails.
Is there any way i could do this?
please see the codes.
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
namespace SeleniumTests
{
[TestFixture]
public class loop
{
private IWebDriver driver;
private StringBuilder verificationErrors;
private string baseURL;
private bool acceptNextAlert = true;
[SetUp]
public void SetupTest()
{
driver = new FirefoxDriver();
baseURL = "http://something.com/";
verificationErrors = new StringBuilder();
}
[TearDown]
public void TeardownTest()
{
try
{
driver.Quit();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
Assert.AreEqual("", verificationErrors.ToString());
}
[Test]
public void TheSignupTest()
{
driver.Navigate().GoToUrl(baseURL + "/Account/Login?ReturnUrl=%2f");
driver.FindElement(By.Id("UserName")).Clear();
driver.FindElement(By.Id("UserName")).SendKeys("something");
driver.FindElement(By.Id("Password")).Clear();
driver.FindElement(By.Id("Password")).SendKeys("something");
driver.FindElement(By.CssSelector("input.btn.small")).Click();
driver.FindElement(By.Id("nav2")).Click();
driver.FindElement(By.Id("upload-file")).Click();
driver.FindElement(By.Id("Name")).Clear();
driver.FindElement(By.Id("Name")).SendKeys("00");
driver.FindElement(By.Id("ReferenceNumberPrefix")).Clear();
driver.FindElement(By.Id("ReferenceNumberPrefix")).SendKeys("00");
driver.FindElement(By.Name("submit")).Click();
}
private bool IsElementPresent(By by)
{
try
{
driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
private bool IsAlertPresent()
{
try
{
driver.SwitchTo().Alert();
return true;
}
catch (NoAlertPresentException)
{
return false;
}
}
private string CloseAlertAndGetItsText()
{
try
{
IAlert alert = driver.SwitchTo().Alert();
string alertText = alert.Text;
if (acceptNextAlert)
{
alert.Accept();
}
else
{
alert.Dismiss();
}
return alertText;
}
finally
{
acceptNextAlert = true;
}
}
}
}
I think a good start would be to transform your test into a parameterized test and use the TestCaseAttribute.
Your test could then look like this:
[Test]
[TestCase("Clayton", "abc", true)]
[TestCase("Clayton", "def", false)]
public void TheSignupTest(string username, string password, bool isAccepted)
{
driver.Navigate().GoToUrl(baseURL + "/Account/Login?ReturnUrl=%2f");
driver.FindElement(By.Id("UserName")).Clear();
driver.FindElement(By.Id("UserName")).SendKeys(username);
driver.FindElement(By.Id("Password")).Clear();
driver.FindElement(By.Id("Password")).SendKeys(password);
driver.FindElement(By.CssSelector("input.btn.small")).Click();
driver.FindElement(By.Id("nav2")).Click();
driver.FindElement(By.Id("upload-file")).Click();
driver.FindElement(By.Id("Name")).Clear();
driver.FindElement(By.Id("Name")).SendKeys("00");
driver.FindElement(By.Id("ReferenceNumberPrefix")).Clear();
driver.FindElement(By.Id("ReferenceNumberPrefix")).SendKeys("00");
driver.FindElement(By.Name("submit")).Click();
if (isAccepted)
{
Assert.That(driver.Url, Is.EqualTo(baseURL + "/PageWhereClientIsRedirectedToAfterSuccessfulLogin"));
}
else
{
Assert.That(driver.FindElement(By.Name("ErrorBox")).Text, Is.EqualTo("Login failed"));
}
}
The test cases are defined using the TestCaseAttribute:
[TestCase("Clayton", "abc", true)]
[TestCase("Clayton", "def", false)]
So you have two test cases in your test runner.

Custom Not-Found Route Fires Only Once

I tend to dislike posting dozens of lines of code and assuming the community at large is interested in untangling my mess. In this case I've exercised everything I can think to search on Google, traced through Glimpse, and Firebug/Fiddler, and what I'm left with is an occasionally working behavior, which is particularly annoying to debug. So, I'm calling out for help.
Here's the gist: I've got a series of classes that handle MVC routes that are otherwise not found (and would produce a 404 error) thanks to #AndrewDavey. I'm attempting to intercept the 404 and show data-driven content where any exists. It all works until I refresh the page. The request works on the first load, but it never fires again after that.
If you're bored or have an itch, the entire code block is below.
Setup goes like this:
Add WebActivator via NuGet
In your AppStart folder add a cs file with the code below
Add a "PageContext" connection string to your web.config
Run the app, the default MVC screen shows up
Now add "/abc" to the end of the url (i.e http://localhost/abc)
A cshtml view, stored in the database, will render.
Change the view's markup in the database and reload the page. Notice no change in your browser.
the /abc route assumes you have a record in the database with the following
Path: "~/abc/index.cshtml"
View: "#{ Layout = null;}<!doctype html><html><head><title>abc</title></head><body><h2>About</h2></body></html>"
I've got no idea why the first request works and subsequent requests don't hit break points and serve up stale content.
My suspicions are:
Some voodoo with the VirtualFile
Something cached (but where?)
A misconfigured handler
Thanks for the help - here's the code (as I shamefully tuck my tail for posting this much code).
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using SomeCms;
[assembly: WebActivator.PreApplicationStartMethod(typeof(Sample.Web.App_Start.cms), "PreStart")]
namespace Sample.Web.App_Start
{
public static class cms
{
public static void PreStart()
{
DynamicModuleUtility.RegisterModule(typeof(InstallerModule));
}
}
}
namespace SomeCms
{
class ActionInvokerWrapper : IActionInvoker
{
readonly IActionInvoker actionInvoker;
public ActionInvokerWrapper(IActionInvoker actionInvoker)
{
this.actionInvoker = actionInvoker;
}
public bool InvokeAction(ControllerContext controllerContext, string actionName)
{
if (actionInvoker.InvokeAction(controllerContext, actionName))
{
return true;
}
// No action method was found.
var controller = new CmsContentController();
controller.ExecuteCmsContent(controllerContext.RequestContext);
return true;
}
}
class ControllerFactoryWrapper : IControllerFactory
{
readonly IControllerFactory factory;
public ControllerFactoryWrapper(IControllerFactory factory)
{
this.factory = factory;
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
try
{
var controller = factory.CreateController(requestContext, controllerName);
WrapControllerActionInvoker(controller);
return controller;
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == 404)
{
return new CmsContentController();
}
throw;
}
}
static void WrapControllerActionInvoker(IController controller)
{
var controllerWithInvoker = controller as Controller;
if (controllerWithInvoker != null)
{
controllerWithInvoker.ActionInvoker = new ActionInvokerWrapper(controllerWithInvoker.ActionInvoker);
}
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return factory.GetControllerSessionBehavior(requestContext, controllerName);
}
public void ReleaseController(IController controller)
{
factory.ReleaseController(controller);
}
}
class InstallerModule : IHttpModule
{
static bool installed;
static readonly object installerLock = new object();
public void Init(HttpApplication application)
{
if (installed)
{
return;
}
lock (installerLock)
{
if (installed)
{
return;
}
Install();
installed = true;
}
}
static void Install()
{
Database.SetInitializer(new CreateDatabaseIfNotExists<PageContext>());
HostingEnvironment.RegisterVirtualPathProvider(new ExampleVirtualPathProvider());
WrapControllerBuilder();
AddNotFoundRoute();
AddCatchAllRoute();
}
static void WrapControllerBuilder()
{
ControllerBuilder.Current.SetControllerFactory(new ControllerFactoryWrapper(ControllerBuilder.Current.GetControllerFactory()));
}
static void AddNotFoundRoute()
{
// To allow IIS to execute "/cmscontent" when requesting something which is disallowed,
// such as /bin or /add_data.
RouteTable.Routes.MapRoute(
"CmsContent",
"cmscontent",
new { controller = "CmsContent", action = "CmsContent" }
);
}
static void AddCatchAllRoute()
{
RouteTable.Routes.MapRoute(
"CmsContent-Catch-All",
"{*any}",
new { controller = "CmsContent", action = "CmsContent" }
);
}
public void Dispose() { }
}
public class CmsContentController : IController
{
public void Execute(RequestContext requestContext)
{
ExecuteCmsContent(requestContext);
}
public void ExecuteCmsContent(RequestContext requestContext)
{
//new CmsContentViewResult().ExecuteResult(new ControllerContext(requestContext, new FakeController()));
new CmsContentViewResult().ExecuteResult(new ControllerContext(requestContext, new FakeController()));
}
// ControllerContext requires an object that derives from ControllerBase.
// NotFoundController does not do this.
// So the easiest workaround is this FakeController.
class FakeController : Controller { }
}
public class CmsContentHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
var routeData = new RouteData();
routeData.Values.Add("controller", "CmsContent");
var controllerContext = new ControllerContext(new HttpContextWrapper(context), routeData, new FakeController());
var cmsContentViewResult = new CmsContentViewResult();
cmsContentViewResult.ExecuteResult(controllerContext);
}
public bool IsReusable
{
get { return false; }
}
// ControllerContext requires an object that derives from ControllerBase.
class FakeController : Controller { }
}
public class CmsContentViewResult : ViewResult
{
public CmsContentViewResult()
{
ViewName = "index";
}
public override void ExecuteResult(ControllerContext context)
{
var request = context.HttpContext.Request;
if (request != null && request.Url != null)
{
var url = request.Url.OriginalString;
ViewData["RequestedUrl"] = url;
ViewData["ReferrerUrl"] = (request.UrlReferrer != null && request.UrlReferrer.OriginalString != url)
? request.UrlReferrer.OriginalString
: null;
}
base.ExecuteResult(context);
}
}
public class ExampleVirtualPathProvider : VirtualPathProvider
{
private readonly List<SimpleVirtualFile> virtualFiles = new List<SimpleVirtualFile>();
public ExampleVirtualPathProvider()
{
var context = new PageContext();
var pages = context.Pages.ToList();
foreach (var page in pages)
{
virtualFiles.Add(new SimpleVirtualFile(page.Path));
}
}
public override bool FileExists(string virtualPath)
{
var files = (from f in virtualFiles
where f.VirtualPath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) ||
f.RelativePath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase)
select f)
.ToList();
return files.Count > 0 || base.FileExists(virtualPath);
}
private class SimpleVirtualFile : VirtualFile
{
public SimpleVirtualFile(string filename) : base(filename)
{
RelativePath = filename;
}
public override Stream Open()
{
var context = new PageContext();
var page = context.Pages.FirstOrDefault(p => p.Path == RelativePath);
return new MemoryStream(Encoding.ASCII.GetBytes(page.View), false);
}
public string RelativePath { get; private set; }
}
private class SimpleVirtualDirectory : VirtualDirectory
{
public SimpleVirtualDirectory(string virtualPath)
: base(virtualPath)
{
}
public override IEnumerable Directories
{
get { return null; }
}
public override IEnumerable Files
{
get
{
return null;
}
}
public override IEnumerable Children
{
get { return null; }
}
}
public override VirtualFile GetFile(string virtualPath)
{
var files = (from f in virtualFiles
where f.VirtualPath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) ||
f.RelativePath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase)
select f).ToList();
return files.Count > 0
? files[0]
: base.GetFile(virtualPath);
}
public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
{
return IsPathVirtual(virtualPath) ? null : base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}
private bool IsPathVirtual(string virtualPath)
{
var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
return
virtualFiles.Any(f => checkPath.StartsWith(virtualPath, StringComparison.InvariantCultureIgnoreCase)) ||
virtualFiles.Any(f => checkPath.Replace("~", "").StartsWith(virtualPath, StringComparison.InvariantCultureIgnoreCase));
}
public override bool DirectoryExists(string virtualDir)
{
return IsPathVirtual(virtualDir) || Previous.DirectoryExists(virtualDir);
}
public override VirtualDirectory GetDirectory(string virtualDir)
{
return IsPathVirtual(virtualDir)
? new SimpleVirtualDirectory(virtualDir)
: Previous.GetDirectory(virtualDir);
}
}
public class ContentPage
{
public int Id { get; set; }
public string Path { get; set; }
public string View { get; set; }
}
public class PageContext : DbContext
{
public DbSet<ContentPage> Pages { get; set; }
}
}
This question turns out to be a non-issue. My oversight of the cache dependency in the virtual path provider is returning null for virtual paths. As such, the view is cached indefinitely.
The solution is to use a custom cache dependency provider that expires immediately.
public class NoCacheDependency : CacheDependency
{
public NoCacheDependency()
{
NotifyDependencyChanged(this, EventArgs.Empty);
}
}
public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
{
return IsPathVirtual(virtualPath) ? new NoCacheDependency() : base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}

Resources