Is it possible to iterate the OutputCache keys? I know you can remove them individually via HttpResponse.RemoveOutputCacheItem(), but is there a way I can iterate all the keys to see what's in the collection?
I searched through Object Viewer, but didn't see anything.
Worst case, I can maintain my own index. Since I'm doing everything by VaryByCustom, they get "fed" through a method in global.asax. It just strikes me that there has to be a more elegant way of doing this.
If you're using ASP.NET 4.0 you could do this by writing your own OutputCacheProvider. That would give you the ability to store the keys at the point that the item is cached:
namespace StackOverflowOutputCacheProvider
{
public class SOOutputCacheProvider: OutputCacheProvider
{
public override object Add(string key, object entry, DateTime utcExpiry)
{
// Do something here to store the key
// Persist the entry object in a persistence layer somewhere e.g. in-memory cache, database, file system
return entry;
}
...
}
}
You'd then be able to read the keys out from wherever you've stored them.
This can be accomplished by inheriting MemoryCache and exposing the enumerator via a custom OutputCacheProvider implementation. Keep in mind that the enumerator locks the cache. Enumerating over the cache should be executed infrequently.
namespace Caching
{
internal class MemoryCacheInternal : System.Runtime.Caching.MemoryCache
{
public MemoryCacheInternal(string name, System.Collections.Specialized.NameValueCollection config = null) : base(name, config)
{
}
public System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> Enumerator()
{
return base.GetEnumerator();
}
}
}
Implement a custom OutputCacheProvider
using System.Web.Caching;
using System.Collections.Generic;
namespace Caching
{
public class EnumerableMemoryOutputCacheProvider : OutputCacheProvider, IEnumerable<KeyValuePair<string, object>>, IDisposable
{
private static readonly MemoryCacheInternal _cache = new MemoryCacheInternal("EnumerableMemoryOutputCache");
public override object Add(string key, object entry, System.DateTime utcExpiry)
{
return _cache.AddOrGetExisting(key, entry, UtcDateTimeOffset(utcExpiry));
}
public override object Get(string key)
{
return _cache.Get(key);
}
public override void Remove(string key)
{
_cache.Remove(key);
}
public override void Set(string key, object entry, System.DateTime utcExpiry)
{
_cache.Set(key, entry, UtcDateTimeOffset(utcExpiry));
}
public IEnumerator<KeyValuePair<string,object>> GetEnumerator()
{
return _cache.Enumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _cache.Enumerator();
}
private DateTimeOffset UtcDateTimeOffset(System.DateTime utcExpiry)
{
DateTimeOffset dtOffset = default(DateTimeOffset);
if ((utcExpiry.Kind == DateTimeKind.Unspecified)) {
dtOffset = DateTime.SpecifyKind(utcExpiry, DateTimeKind.Utc);
} else {
dtOffset = utcExpiry;
}
return dtOffset;
}
#region "IDisposable Support"
// To detect redundant calls
private bool disposedValue;
// IDisposable
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue) {
if (disposing) {
_cache.Dispose();
}
}
this.disposedValue = true;
}
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
Configure the custom OutputCacheProvider
<system.web>
<caching>
<outputCache defaultProvider="EnumerableMemoryCache">
<providers>
<add name="EnumerableMemoryCache"
type="Caching.EnumerableMemoryOutputCacheProvider, MyAssemblyName"/>
</providers>
</outputCache>
<outputCacheSettings>
<outputCacheProfiles>
<add name="ContentAllParameters" enabled="false" duration="14400" location="Any" varyByParam="*"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
Enumerate over the cache, in this case removing cache items.
OutputCacheProvider provider = OutputCache.Providers[OutputCache.DefaultProviderName];
if (provider == null) return;
IEnumerable<KeyValuePair<string, object>> keyValuePairs = provider as IEnumerable<KeyValuePair<string, object>>;
if (keyValuePairs == null) return;
foreach (var keyValuePair in keyValuePairs)
{
provider.Remove(keyValuePair.Key);
}
I have use this
http://www.codeproject.com/KB/session/exploresessionandcache.aspx
to view the cache and the session data. I only have to say that show only one pools data. If you have more pools, then you just see the one you are on it.
Related
I was using the following code to add roles of the user.
Roles.AddUserToRole(model.Email, "COMPANYVIP");
and then i got this error:
The Role Manager feature has not been enabled
after some research i found out that we have to add the following connection string in web.config
<configuration>
<system.web>
<roleManager enabled="true" />
</system.web>
</configuration>
adding this eliminated my first error but now i get this error:
A network-related or instance-specific error occurred while establishing a connection to SQL Server
what should i do now?
Remove your change in web.config and in Startup.Auth add the following reference to ConfigureAuth:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
// Add this reference to RoleManager (without changing any other items)
// Make sure it is added below ApplicationDbContext.Create
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
}
Then in your Controller, make sure it includes this in the constructor:
public class YourController : Controller
{
// Add this
private ApplicationRoleManager _roleManager;
// Add roleManager
public YourController(ApplicationRoleManager roleManager)
{
// Add this
RoleManager = roleManager;
}
public ApplicationRoleManager RoleManager {
get {
return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set {
_roleManager = value;
}
}
}
and also include this in the Controller's Dispose (if you have it):
protected override void Dispose(bool disposing)
{
if (disposing)
{
// include this
if (_roleManager != null)
{
_roleManager.Dispose();
_roleManager = null;
}
}
base.Dispose(disposing);
}
You may also need to add this code to IdentityConfig (in the App_Start folder if you're using the template):
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{ }
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options,
IOwinContext context)
{
var manager = new ApplicationRoleManager(
new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
return manager;
}
}
You should now be able to use the RoleManager in the Controller.
My simple repository's getAll method:
public List<ListModel> GetAllLists()
{
using (MySqlConnection connection = new MySqlConnection(this.connectionString))
{
return connection.Query<ListModel>("SELECT * FROM projectx.lists").AsList();
}
}
I'm using this class I've found here in so to handle caching:
public class CacheUtils : ICacheService
{
public TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback, double durationInMinutes = 120) where TValue : class
{
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
Debug.WriteLine("Not cached");
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
else
Debug.WriteLine("Cached!");
return item;
}
public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback, double durationInMinutes = 120) where TValue : class
{
string cacheKey = string.Format(cacheKeyFormat, id);
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback(id);
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
}
Home controller:
public ActionResult Index()
{
ListRepository listRep = new ListRepository();
CacheUtils cache = new CacheUtils();
return View(cache.Get("lists", listRep.GetAllLists));
}
Question, is there a better way of handling cache than calling the helper from the controller? Ideally, it should be inside the repository method. But do I need to repeat the check for existing cache data on every single method of the repository? Ie.:
public List<ListModel> GetAllLists()
{
var lists = Cache["lists"];
if(lists == null)
{
using (MySqlConnection connection = new MySqlConnection(this.connectionString))
{
lists = connection.Query<ListModel>("SELECT * FROM projectx.lists").AsList();
}
Cache["lists"] = lists;
}
return ((List<ListModel>)lists);
}
Use a decorator pattern and don't polute business or ui with caching logic.
Wire it up with something like ninject (or poor bastards if you dont want to add a DI) I'd recommend marking it as single instance.
Benefits include:
Adding a invalidating method like void Save(ListModel) is easy to
invalidate the cache.
Your top layer and bottom layer know nothing
about the fact they have been cached.
You can also decorate again to add in logging, profiling, etc
You can also control the caching life cycle
you don't polute the controller level with caching logic
easy to remove
So something like the below would work. For how too add decorators in ninject see https://stackoverflow.com/a/8910599/1073280
public class MyHomeController
{
private readonly IListCrud _listcrud;
public MyHomeController(IListCrud listcrud)
{
_listcrud = listcrud;
}
public ActionResult Index()
{
return View(_listcrud.GetAllLists());
}
}
public interface IListCrud
{
List<ListModel> GetAllLists();
}
public class ListCrud : IListCrud
{
public List<ListModel> GetAllLists()
{
using (MySqlConnection connection = new MySqlConnection(this.connectionString))
{
return connection.Query<ListModel>("SELECT * FROM projectx.lists").AsList();
}
}
}
public class ListCrudCache : IListCrud
{
private readonly ICacheService _cache;
private readonly IListCrud _inner;
public ListCrudCache(ICacheService cache, IListCrud inner)
{
_cache = cache;
_inner = inner;
}
public List<ListModel> GetAllLists()
{
return _cache.Get("lists", _inner.GetAllLists);
}
}
Opinion: maybe just to keep the code small but be careful using select * with a ORM. if someone renames or removes a column you wont have any easy to unit test/detect failure mechanism.
In my opinion it shouldn't be in the repository, as it (to me) smells like violation or SRP. Caching should be a higher level service above the repository.
You need to think about what actually needs the benefits of the caching. If the caching is for speeding up the WEB API interface, then having it in the controller is the best way by far. If you need caching elsewhere too, consider introducing some middle layer service classes and put caching there, but I would always make it optional in some way.
suppose i have one static method and i need to access viewstate from that method...how could i do so...i know it is not possible but there must be some way out.
[WebMethod]
public static string GetData(int CustomerID)
{
string outputToReturn = "";
ViewState["MyVal"]="Hello";
return outputToReturn;
}
You can get the reference to the page via HttpContext.CurrentHandler. But since Control.ViewState is protected you can't access it (without using reflection) as opposed to the Session which is accessible via HttpContext.Current.Session.
So either don't use a static method, use the Session or use this reflection approach:
public static string CustomerId
{
get { return (string)GetCurrentPageViewState()["CustomerId"]; }
set { GetCurrentPageViewState()["CustomerId"] = value; }
}
public static System.Web.UI.StateBag GetCurrentPageViewState()
{
Page page = HttpContext.Current.Handler as Page;
var viewStateProp = page?.GetType().GetProperty("ViewState",
BindingFlags.FlattenHierarchy |
BindingFlags.Instance |
BindingFlags.NonPublic);
return (System.Web.UI.StateBag) viewStateProp?.GetValue(page);
}
However, this won't work if called via WebService, because then it's outside of Page-Lifecycle.
You might be able use [WebMethod(EnableSession=true)] for your PageMethod, and use Session instead of ViewState. Remember, with a static PageMethod no instance of the Page class is ever created, so nice things like ViewState simply are not there and there is no way to make them be there.
I tried this and worked for me:
Create a class conteining the properties of the viewState you want to access to
In the constructor pass the real ViewState
Create a static instance of the class but not initialize it
In the PageLoad initialize Not static class and the static one
Access the ViewState using static class properties
public class Repository
{
public int a
{
get
{
if (_viewState["a"] == null)
{
return null;
}
return (int)_viewState["a"];
}
set
{
_viewState["a"] = value;
}
}
public StateBag _viewState;
public Repository(StateBag viewState)
{
_viewState = viewState;
}
}
static Repository staticRepo;
protected void Page_Load(object sender, EventArgs e)
{
Repository repo = new Repository(ViewState);
staticRepo = repo;
}
public static void testMethod()
{
int b = staticRepo.a;
}
i have a .NET 3.5 solution with an asp.net(web site) project with fluentnhibernate and it's test project(class library project).i've referenced the asp.net project in the test project and with all fluentnhibernate/nhibenate dlls.
What i fail to comprehend is that, on a run of a webform (hit from browser) let's say Test.aspx, building of schema is successfull and i could see the tables in my database.
here is the method i call on Test.aspx.cs
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ISession session = SessionManager.Instance.OpenSession();
SessionManager.BuildSchema(session);
}
}
i happen to use the same method in CanGenerateSchema method of my test class and it always fails
[TestFixture]
public class CanGenerateSchemaTestSuite
{
[Test]
public void CanGenarateSchema()
{
ISession session = SessionManager.Instance.OpenSession();
SessionManager.BuildSchema(session);
}
}
here is the SessionManager i use :
public sealed class SessionManager
{
private readonly ISessionFactory _sessionFactory;
public static ISessionFactory SessionFactory
{
get { return Instance._sessionFactory; }
}
private ISessionFactory GetSessionFactory()
{
return _sessionFactory;
}
public static SessionManager Instance
{
get { return NestedSessionManager._sessionManager; }
}
public ISession OpenSession()
{
return Instance.GetSessionFactory().OpenSession();
}
private static Configuration SaveConfigs;
private SessionManager()
{
try
{
if (_sessionFactory == null)
{
//from the debugging the code breaks from here when trying to get connectionstring.
string constring = ConfigurationManager.AppSettings["localdb"].ToString();
FluentConfiguration configuration = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2005.ConnectionString(constring))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<myproject.model.Request>();
m.FluentMappings.AddFromAssemblyOf<myproject.model.Route>();
})
.ExposeConfiguration((x) =>
{
SaveConfigs = x;
x.SetProperty("current_session_context_class", "thread_static");
});
_sessionFactory = configuration.BuildSessionFactory();
}
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
}
public static void BuildSchema(ISession session)
{
var export = new SchemaExport(SaveConfigs);
export.Execute(false,true,false,session.Connection,null);
}
class NestedSessionManager
{
internal static readonly SessionManager _sessionManager = new SessionManager();
}
}
So from my comment the NullReferenceException happens when accessing the connectionstring. I don't have the explanation on why that happens.I'm sure it's some kind of gotchas, i can't get over it.I would be very grateful if anyone could give me a hand here.thanks for reading.
ConfigurationManager.AppSettings["localdb"] from Test.aspx would be pulling from your web.config file on a web project.
That file wouldn't be accessible to your test project (I'm assuming your tests are in a separate project from your web site). You should be able to get around this by adding an app.config file into your test project with the correct localdb settings or rather than using a configuration string, use FluentNHibernate's fluent builder.
Example app.config file:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="localdb" value="yourconnectionstring" />
</appSettings>
</configuration>
If the value returned by ConfigurationManager.AppSettings["localdb"] is null then the .ToString() call will cause the NullReferenceException.
Ensure the "localdb" setting exists.
I have the Global.asax like the code below:
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
// ....
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(typeof(IOCControllerFactory));
}
}
public class IOCControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public IOCControllerFactory()
{
kernel = new StandardKernel(new NanocrmContainer());
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return base.GetControllerInstance(requestContext, controllerType);
var controller = kernel.TryGet(controllerType) as IController;
if (controller == null)
return base.GetControllerInstance(requestContext, controllerType);
var standartController = controller as Controller;
if (standartController is IIoCController)
((IIoCController)standartController).SetIoc(kernel);
return standartController;
}
class NanocrmContainer : Ninject.Modules.NinjectModule
{
public override void Load()
{
// ...
Bind<DomainModel.Entities.db>().ToSelf().InRequestScope().WithConstructorArgument("connection", "Data Source=lims;Initial Catalog=nanocrm;Persist Security Info=True;User ID=***;Password=***");
}
}
}
In this case if somewhere it is the class, defined like:
public class UserRepository : IUserRepository
{
private db dataContext;
private IUserGroupRepository userGroupRepository;
public UserRepository(db dataContext, IUserGroupRepository userGroupRepository)
{
this.dataContext = dataContext;
this.userGroupRepository = userGroupRepository;
}
}
then the dataContext instance is created (if no one was created in this request scope) by Ninject.
So the trouble now is - where to invoke dataContext method .Dispose()?
UPD:
so i followed the advice from KeeperOfTheSoul and solved the issue in such way:
public override void ReleaseController(IController controller)
{
base.ReleaseController(controller);
var db = kernel.Get<DomainModel.Entities.db>();
db.Dispose();
}
A good place to handle this is in IControllerFactory.ReleaseController, eg
public override void ReleaseController() {
base.ReleaseController();
//Do whatever you need to clean up the IoC container here
}
In NInject this could be handled by scoping using an activation block, at the start of the request when creating the controller you can store the activation block in the HttpContext's current items, during ReleaseController you can retrieve the previously created activation block and dispose it.
You could also consider using InScope and having the custom scope implement INotifyWhenDisposed. After that the usage is the same as with an activation block, except now you store the scope in the HttpContext's current items.
A pattern that is sometimes used to dispose db connections is to call Dispose from the finaliser.
public class db : IDisposable {
//called by the garbage collector
~db() {
//Call dispose to make sure the resources are cleaned up
Dispose(false);
}
//IDisposable implementation
public void Dispose() {
Dispose(true);
}
//subclasses of db can override Dispose(bool) and clean up their own fields
protected virtual void Dispose (bool disposing) {
if (disposing) {
//Supress finalization as all resources are released by this method
//Calling Dispose on IDisposable members should be done here
GC.SupressFinalize();
}
//Clean up unmanaged resources
//Do not call other objects as they might be already collected if called from the finalizer
}
}
You could hook it into Application_EndRequest.