ASP.Net会话中的静态单例

时间:2011-05-20 18:58:11

标签: asp.net session singleton

我一直在阅读ASP.Net中的单身人士,我已经看到了各种实现和建议。我试图在这个之后对我的实现进行建模:https://stackoverflow.com/...asp-net-singleton

这是我的问题:我希望我实例化的对象持续当前会话的生命,但不会在会话之间共享。例如,如果两个用户同时登录,我希望他们每个“拥有”一个全局对象的实例。以下是我的实施。这是正确的方法吗?

public class AppGlobal
{
    #region Contructors

    public AppGlobal() { }

    public static AppGlobal Instance
    {
        get
        {
            HttpSessionState session = HttpContext.Current.Session;

            if (session["AppGlobalInstance"] == null)
            {
                session["AppGlobalInstance"] = new AppGlobal();
            }

            return (AppGlobal)session["AppGlobalInstance"];
        }
    }

    #endregion

    #region Public Properties

    public User UserObject { get; set; }
    public Campaign CampaignObject { get; set; }
    public List<int> SelectedContactIDs = new List<int>();
    public List<int> UnsubmittedContactIDs = new List<int>();
    public List<int> SubmittedContactIDs = new List<int>();
    public List<int> ProcessedContactIDs = new List<int>();

    #endregion

    #region Public Instance Methods

    public void ClearCampaign()
    {
        CampaignObject = null;
        UnsubmittedContactIDs.Clear();
        SubmittedContactIDs.Clear();
        ProcessedContactIDs.Clear();
        SelectedContactIDs.Clear();
    }
    public void LoadCampaign(int campaignID)
    {
        //Ensure that old data is overwritten
        MailCampaignManagerEntities db = new MailCampaignManagerEntities();

        db.Campaigns.MergeOption = System.Data.Objects.MergeOption.OverwriteChanges;

        //Clear the campaign and associated data
        ClearCampaign();

        //Set campaign object in AppGlobal
        this.CampaignObject = db.Campaigns.SingleOrDefaultasp.net(x => x.CampaignID == campaignID);

        //Populate Contact Status Lists
        this.UnsubmittedContactIDs.AddRange(from x in this.CampaignObject.CampaignContacts
                                                 where x.ContactSubmissionID == null
                                                 select x.CampaignContactID);

        this.SubmittedContactIDs.AddRange(from x in this.CampaignObject.CampaignContacts
                                               where x.ContactSubmissionID != null
                                               select x.CampaignContactID);

        this.ProcessedContactIDs.AddRange(from x in this.CampaignObject.CampaignContacts
                                               where x.ContactSubmissionID != null
                                               && x.ContactSubmission.DateProcessed != null
                                               select x.CampaignContactID);
    }

    #endregion

    #region Public Static Methods

    public static void WriteLogEntry(int? campaignID, int? contactSubmissionID, int? scheduledDropID, int? userID, string activityDescription)
    {
        ActivityLog activityLog = new ActivityLog();
        activityLog.CampaignID = campaignID;
        activityLog.ContactSubmissionID = contactSubmissionID;
        activityLog.ScheduledDropID = scheduledDropID;
        activityLog.UserID = userID;
        activityLog.Text = activityDescription;
        activityLog.CreatedDate = DateTime.Now;

        using (MailCampaignManagerEntities db = new MailCampaignManagerEntities())
        {
            db.ActivityLogs.AddObject(activityLog);
            db.SaveChanges();
        }
    }

    #endregion
}

4 个答案:

答案 0 :(得分:2)

实施通常应该是“好的”,但是......

您应该将对象标记为[Serializable],具体取决于您已配置的SessionStateModule。 Web场或Web园通常使用modules other than the InProc one,它们使用序列化来存储会话状态。否则,您的对象看起来可以序列化,所以没有问题。

您可能想要检查当前是否有任何会话,或者您可以获得NullReferenceException。这可能意味着应用程序配置错误,或者在生命周期中过早地调用。

由于您检查和设置Session变量的方式存在竞争条件,您的应用程序可能会为单个会话分配两次AppGlobal对象。我不认为即使这是目前的问题,但如果你想要包含更多花哨的东西,请记住这一点。为了防止这种情况,您可以像这样使用lock

public class AppGlobal
{
   private static object _syncRoot = new object();

   public static AppGlobal Instance
   {
       get
       {
           HttpSessionState session = HttpContext.Current.Session;

           lock (_syncRoot)
           {
               if (session["AppGlobalInstance"] == null)
               {
                   session["AppGlobalInstance"] = new AppGlobal();
               }
           }

           return (AppGlobal)session["AppGlobalInstance"];
       }
    }     
}

如果您想在禁止序列化的对象中存储任何内容,并且需要支持其他SessionStateModule,则可以将实例存储在使用经典Singleton模式的集合中(here是一个很好的实现) 。 ConcurrentDictionary可能是个好人。作为关键,您可以使用您在会话中执行存储的独特内容,例如GUID。当会话以任何方式结束时,您需要从集合中删除条目。

答案 1 :(得分:0)

这看起来像一个有效的单例模式。它看起来像是一个非常大的对象存储在Session中。如果您有很多用户,可能会遇到性能和内存问题。

答案 2 :(得分:0)

Session中的对象已经是唯一的,这意味着同一个键指的是一个对象实例。

答案 3 :(得分:0)

Session对象基本上来自单例对象的字典。因此,当您引用Session对象时,您已经从场景背后的单例模式中受益。因此,您不需要通过将Session对象放入单例模式来重新发明轮子。