C#我的第一个单例尝试显然不太正确

时间:2019-01-11 19:57:46

标签: c# singleton

在我的网站中,有很多对sql数据库的调用,每次调用时都会传递请求数据的用户ID。我试图建立一个单例类,该类只需要对数据库进行一次调用即可获取该人的ID以及仅需获取一次的其他用户属性。

我以为我所有的设置都正确,并且似乎可以正常工作,所以我将代码更改投入生产,并接到了一些用户的电话,说他们登录时都被识别为我。这是基于我主页顶部的“欢迎”消息,每个人的用户名都显示为我。

我的单身人士在下面。接到电话之后,我开始调试,发现是我认为从未设置过设置值的formValues()方法。唯一被调用的是公共实例方法。我想这对知道他们在做什么的每个人都是显而易见的:)。

我认为这无关紧要,但是变量的实例正在我的代码中被调用,例如:formValues.Instance.firstName。

那么我有多近?这东西可救吗?

public sealed class formValues : System.Web.Services.WebService
{
    static readonly formValues instance = new formValues();

    public string userName;
    public string firstName;
    public string personID;
    public string secBlur;
    public int admin;
    public int fafsa;
    public int staff;

    static formValues()
    {

    }

    formValues()
    {
        //This retrieves the person logged into windows/active directory
        FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
        userName = id.Name;

        // Grab this user's firstname, personID, and Admin status
        string mySQL = "exec get_userData @userName";
        string cf = System.Configuration.ConfigurationManager.ConnectionStrings["DistrictAssessmentDWConnectionString"].ConnectionString;

        SqlConnection connection = new SqlConnection(cf);
        SqlCommand command = new SqlCommand(mySQL, connection);

        command.Parameters.AddWithValue("@userName", userName);

        connection.Open();

        SqlDataReader dr = command.ExecuteReader();

        if (dr.HasRows)
        {
            while (dr.Read())
            {
                personID = dr["personID"].ToString();
                firstName = dr["firstName"].ToString();
                admin = Convert.ToInt32(dr["admin"]);
                secBlur = dr["secBlur"].ToString();
                fafsa = Convert.ToInt32(dr["FAFSA"]);
                staff = Convert.ToInt32(dr["staffSec"]);
            }
        }

        connection.Close();
    }

    public static formValues Instance
    {
        get
        {
            return instance;
        }
    }
}

3 个答案:

答案 0 :(得分:1)

问题在于这些属性(实际上是字段)处于单例状态:

public string userName;
public string firstName;
public string personID;
public string secBlur;
public int admin;
public int fafsa;
public int staff;

...以及在构造函数中填充它们的事实。这意味着您第一次创建该类时,它将被填充。如果您将同一类实例作为单例重用,则构造函数将永远不会再被调用,这意味着填充的值将被设置一次并永久保留。

换句话说,您的用户数据是单例。除非您只有一个用户,否则那不是很好。

首先,我将检索数据的类与包含数据的类分开。

数据可能看起来像这样:

public class UserData
{
    public string UserName { get; set; }
    public string FirstName { get; set; }
    // ..etc...
}

该模型包含数据。它不包含用于填充数据的代码。

然后,创建一个检索数据的类。它不应在构造函数中检索数据。构造函数用于创建类。这不是我们要放置类实际所做的事情的地方。

该类可能具有这样的方法:

public UserData GetUserData()

...将创建UserData的实例并返回它。


对于它的价值,在大多数情况下,我们不需要创建单例。

假设您有此类:

public class SqlUserDataProvider
{
    UserData GetUserData()
    {
     ...
    }
}

如果构造函数不执行任何“繁重的工作”(例如读取数据或文件),则每次需要一个实例时,您就可以创建一个新实例。它的效率不如使用单个实例,但是差异通常很小,因此不值得考虑。

或者,您可以创建一个实例并重新使用它。在那种情况下,您将其用作单例,但从技术上讲,它不是一个。单例是类编写的,因此只能创建它的单个实例。大多数时候,没有理由这样做。严格执行关于如何使用该类的限制是额外的代码行。它可能使代码的可维护性降低并且更难调试。因此,这是无益或消极的额外工作。

答案 1 :(得分:0)

基本上,您想进行缓存。对于http请求,您需要检查值是否已经在缓存中,如果不是,请从数据库中读取,否则请使用缓存。 您可以从内置的Cache

开始

对于单例,最简单的解决方案是使用Lazy实现它们。这是good article

答案 2 :(得分:0)

实例对象在应用程序生存期内仅创建一次。 因此,您获得私有FormsIdentity的私有构造函数formValues()实际上是在应用程序首次启动后才执行一次。

之后,对于所有传入请求,将给出已填充的实例。 因此,对于其他任何请求,您都会看到相同的用户名。

由于需求是基于每个用户的,而不是整个应用程序中的全局需求,因此“单个”可能无济于事。谢谢。