使MVC站点地图每个会话唯一,而不是每个用户

本文关键字:用户 会话 MVC 站点 地图 唯一 | 更新日期: 2023-09-27 18:18:41

我们的MvcSitemap已经实现了一些DynamicNodeProviders

我们希望这些在每个会话中是唯一的。但似乎每个用户都是唯一的。

因此,如果一个用户登录到两个不同的浏览器或计算机,他们目前共享相同的站点地图。

我们不需要这个

但是我似乎不知道如何让它使用用户/会话组合的唯一性。

有办法让这个工作吗?

使MVC站点地图每个会话唯一,而不是每个用户

选项1:

根据会话状态实现自己的ICacheProvider,并使用DI注入。

using System;
using System.Collections.Generic;
using MvcSiteMapProvider.Web.Mvc;
using MvcSiteMapProvider.Caching;
using System.Web;
public class SessionStateCacheProvider<T>
    : ICacheProvider<T>
{
    public SessionStateCacheProvider(
        IMvcContextFactory mvcContextFactory
        )
    {
        if (mvcContextFactory == null)
            throw new ArgumentNullException("mvcContextFactory");
        this.mvcContextFactory = mvcContextFactory;
    }
    private readonly IMvcContextFactory mvcContextFactory;
    protected HttpContextBase Context
    {
        get
        {
            return this.mvcContextFactory.CreateHttpContext();
        }
    }
    #region ICacheProvider<ISiteMap> Members
    public bool Contains(string key)
    {
        return (Context.Session[key] != null);
    }
    public Caching.LazyLock Get(string key)
    {
        return (LazyLock)Context.Session[key];
    }
    public bool TryGetValue(string key, out Caching.LazyLock value)
    {
        value = this.Get(key);
        if (value != null)
        {
            return true;
        }
        return false;
    }
    public void Add(string key, LazyLock item, ICacheDetails cacheDetails)
    {
        // NOTE: cacheDetails is normally used to set the timeout - you might
        // need to roll your own method for doing that.
        Context.Session[key] = item;
    }
    public void Remove(string key)
    {
        Context.Session.Remove(key);
    }
    public event EventHandler<MicroCacheItemRemovedEventArgs<T>> ItemRemoved;
    #endregion
    // NOTE: Normally this is called by a callback from the cache when an item exprires.
    // It is required to ensure there is no memory leak because a sitemap has circular references
    // that need to be broken explicitly. You need to work out how to call this when the user's session
    // expires.
    protected virtual void OnCacheItemRemoved(MicroCacheItemRemovedEventArgs<T> e)
    {
        if (this.ItemRemoved != null)
        {
            ItemRemoved(this, e);
        }
    }
}

然后像这样注入(StructureMap示例所示):

// Setup cache
SmartInstance<CacheDetails> cacheDetails;
this.For<ICacheProvider<ISiteMap>>().Use<SessionStateCacheProvider<ISiteMap>>();
var cacheDependency =
    this.For<ICacheDependency>().Use<NullCacheDependency>();
cacheDetails =
    this.For<ICacheDetails>().Use<CacheDetails>()
        .Ctor<TimeSpan>("absoluteCacheExpiration").Is(absoluteCacheExpiration)
        .Ctor<TimeSpan>("slidingCacheExpiration").Is(TimeSpan.MinValue)
        .Ctor<ICacheDependency>().Is(cacheDependency);

选项2:

将用户名附加到自定义ISiteMapCacheKeyGenerator中的siteMapCacheKey,并通过DI注入:

public class SessionBasedSiteMapCacheKeyGenerator
    : ISiteMapCacheKeyGenerator
{
    public UserBasedSiteMapCacheKeyGenerator(
        IMvcContextFactory mvcContextFactory
        )
    {
        if (mvcContextFactory == null)
            throw new ArgumentNullException("mvcContextFactory");
        this.mvcContextFactory = mvcContextFactory;
    }
    protected readonly IMvcContextFactory mvcContextFactory;
    #region ISiteMapCacheKeyGenerator Members
    public virtual string GenerateKey()
    {
        var context = mvcContextFactory.CreateHttpContext();
        var builder = new StringBuilder();
        builder.Append("sitemap://");
        builder.Append(context.Request.Url.DnsSafeHost);
        builder.Append("/?sessionId=");
        builder.Append(context.Session.SessionID);
        return builder.ToString();
    }
    #endregion
}

像这样注入(StructureMap的例子):

this.For<ISiteMapCacheKeyGenerator>().Use<SessionBasedSiteMapCacheKeyGenerator>();

注意,需要使用外部DI容器。

请在这里看到我的开放问题,并向我解释为什么你想在GitHub上这样做,因为它使大多数功能无用:https://github.com/maartenba/MvcSiteMapProvider/issues/16#issuecomment-22229604