C# 为.NET3.5站点提供更好的缓存

C# 为.NET3.5站点提供更好的缓存,c#,.net-3.5,asp.net-3.5,C#,.net 3.5,Asp.net 3.5,我正在尝试优化旧的应用程序,现在我正在尝试将SQL请求减少到最小。 我创建了简单的缓存机制,但它需要3种方法: public static List<Models.CarModel> GetCarModels() { return GetCarModels(false); } public static List<Models.CarModel> GetCarModels(bool reload) { const string my_key = "Get

我正在尝试优化旧的应用程序,现在我正在尝试将SQL请求减少到最小。 我创建了简单的缓存机制,但它需要3种方法:

public static List<Models.CarModel> GetCarModels()
{
    return GetCarModels(false);
}

public static List<Models.CarModel> GetCarModels(bool reload)
{
    const string my_key = "GetCarModels";
    object list = HttpContext.Current.Cache[my_key] as List<Models.CarModel>;
    if ((reload) || (list == null))
    {
        list = GetCarModels_P();
        HttpContext.Current.Cache.Insert(my_key, list, null, DateTime.Now.AddHours(1), TimeSpan.Zero);
    }
    return (List<Models.CarModel>)list;
}

private static List<Models.CarModel> GetCarModels_P()
{
    var tmp_list = new List<Models.CarModel>();

    using (var conn = new SqlConnection())
    {
        conn.ConnectionString = ConfigurationManager.ConnectionStrings["HelpDesk"].ToString();
        using (var cmd = new SqlCommand(@"SELECT_CAR_MODELS", conn))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandTimeout = 360;
            conn.Open();
            using (SqlDataReader sdr = cmd.ExecuteReader())
            {
                while (sdr.Read())
                {
                    var carModel = new Models.CarModel()
                    {
                        Id = Convert.ToInt32(sdr["Id"]),
                        Name= Convert.ToString(sdr["CarName"])
                    };
                    tmp_list.Add(carModel );
                }
            }
            conn.Close();
        }
    }
    return tmp_list;
}
publicstaticlist GetCarModels()
{
返回GetCarModels(false);
}
公共静态列表GetCarModels(bool重载)
{
常量字符串my_key=“GetCarModels”;
object list=HttpContext.Current.Cache[my_key]为列表;
如果((重新加载)| |(列表==null))
{
list=GetCarModels_P();
HttpContext.Current.Cache.Insert(my_key,list,null,DateTime.Now.AddHours(1),TimeSpan.Zero);
}
返回(列表)列表;
}
私有静态列表GetCarModels_P()
{
var tmp_list=新列表();
使用(var conn=new SqlConnection())
{
conn.ConnectionString=ConfigurationManager.ConnectionString[“帮助热线”].ToString();
使用(var cmd=new SqlCommand(@“选择汽车模型”,康涅狄格州))
{
cmd.CommandType=CommandType.storedProcess;
cmd.CommandTimeout=360;
conn.Open();
使用(SqlDataReader sdr=cmd.ExecuteReader())
{
while(sdr.Read())
{
var carModel=新模型。carModel()
{
Id=转换为32(sdr[“Id”]),
Name=Convert.ToString(sdr[“CarName”])
};
tmp_列表添加(carModel);
}
}
康涅狄格州关闭();
}
}
返回tmp_列表;
}
这很好,sql查询结果缓存1小时,但对于每个旧方法,我必须创建3个新方法,因此对于10个旧方法,我必须编写30个新方法。
我想减少我必须编写的代码数量,可能有一种方法可以创建读取/写入缓存的通用方法


GetCarModels\u p
可能不会更改,但我的2个公共方法可以优化(我要求)。

您可以使用泛型和lambda来简化更改:

public static List<T> GetViaCache<T>(bool reload, string key, Func<List<T>> loader)
{
    object list = HttpContext.Current.Cache[key] as List<T>;
    if ((reload) || (list == null))
    {
        list = loader();
        HttpContext.Current.Cache.Insert(key, list, null, DateTime.Now.AddHours(1), TimeSpan.Zero);
    }
    return list;
}
公共静态列表GetViaCache(bool重载、字符串键、Func加载程序)
{
对象列表=HttpContext.Current.Cache[key]作为列表;
如果((重新加载)| |(列表==null))
{
list=loader();
HttpContext.Current.Cache.Insert(key,list,null,DateTime.Now.AddHours(1),TimeSpan.Zero);
}
退货清单;
}
这简化了加载代码,如下所示

public static List<Models.CarModel> GetCarModels(bool reload)
{
    return GetViaCache<Models.CarModel>(reload, "GetCarModels", GetCarModels_P);
}
publicstaticlist-GetCarModels(bool-reload)
{
返回GetViaCache(重新加载“GetCarModels”,GetCarModels_P);
}

当然,您仍然需要为缓存生成一个唯一的键。

您可以在助手类中实现该模式,如下所示:

internal class Cached<T> where T: class
{
    private readonly Func<T> _loadFunc;
    private readonly string _key;
    private readonly TimeSpan _expiration;

    public Cached(Func<T> loadFunc, string key, TimeSpan expiration)
    {
        _loadFunc = loadFunc;
        _key = key;
        _expiration = expiration;
    }

    public T GetValue(bool reload)
    {
        T value = (T)HttpContext.Current.Cache[_key];

        if (reload || value == null)
        {
            value = _loadFunc();
            HttpContext.Current.Cache.Insert(_key, value, null,
                DateTime.Now + _expiration, TimeSpan.Zero);
        }

        return value;
    }
}
缓存的内部类,其中T:class
{
私有只读函数_loadFunc;
私有只读字符串\u密钥;
私有只读TimeSpan\u过期;
公共缓存(Func loadFunc,字符串键,TimeSpan过期)
{
_loadFunc=loadFunc;
_钥匙=钥匙;
_到期=到期;
}
公共T GetValue(布尔重新加载)
{
T value=(T)HttpContext.Current.Cache[_key];
如果(重新加载| |值==null)
{
值=_loadFunc();
HttpContext.Current.Cache.Insert(_key,value,null,
DateTime.Now+\u过期,TimeSpan.Zero);
}
返回值;
}
}
您可以这样使用它:

private static Cached<List<Models.CarModel>> _carModels =
      new Cached<List<Models.CarModel>>(
          GetCarModels_P, "GetCarModels", TimeSpan.FromHours(1));

public static List<Models.CarModel> GetCarModels()
{
    return _carModels.GetValue(false);
}
private static Cached\u carModels=
新缓存(
GetCarModels_P,“GetCarModels”,TimeSpan.FromHours(1));
公共静态列表GetCarModels()
{
返回_carModels.GetValue(false);
}

当然,你也可以只使用一个通用的方法(就像我写这篇文章时发布的其他答案之一)。这取决于我将使用的情况。如果您有一个类似的调用强制重新加载,我的方法会简单一些,因为您不必重复自己的操作。然而,在缺点方面,我的实现有点表明,
缓存的
实例本身正在个人保留数据,而实际上是HttpContext缓存在这样做。

您刚刚写了两个非常好的理由,说明为什么应该使用像NHibernate或Entity Framework这样的ORM层。它们消除了编写大量样板SQL调用函数的痛苦。他们支持缓存,我知道这是一个简单的事实,与NH馅饼,不知道EF


无需丢弃任何现有代码,ORM可以与现有数据库代码共存,甚至可以使用完全相同的连接。您可以一个接一个地慢慢地将现有函数从徒手SQL迁移到您选择的ORM。如果你想花时间优化一个旧的应用程序,我会说这是最好的方法。

我修复了发布的
GetCarModels(bool reload)
代码中的一个小错误,我想它的意思是这样的。@Thorarin谢谢,我从波兰语转换了字段和方法名,并输入了一些错误:)我倾向于同意,尽管这有点取决于优化OP的原因。虽然我认为NHibernate和EF都是很好的工具,但它们并不比“哑”代码和
DataReader
快。如果你主要想减少数据库的压力,我会说去做。我相信ORM层额外的CPU周期与数据库查询的延迟相比是微不足道的。在新项目中,我使用EF,但我目前正在尝试优化的这个项目是一个次要项目。这是一个网站项目,我不想添加另一个额外的图书馆到它。所以现在我要写一个简单的助手类,但我必须同意ORM是一个不错的选择!感谢您的代码和描述,我将尝试两种方法。您的代码一开始看起来更复杂,但我可以轻松地将所有方法添加到单个静态类中,并从任意位置调用它。这与我试图做的最接近,但我必须说@Thorarin代码让我印象深刻,我将首先尝试他的方法,如果我遇到一些问题,我会选择你的解决方案:)这几乎是相同的方法,@Misiu