如何使用Entity Framework 6

时间:2015-08-28 17:33:38

标签: c# entity-framework caching

我正在学习EF的方式,而且我知道缓存比在国家,国家等地方往返DB要快。但我不确定如何实现它。我正在看这篇文章(entities from local cache),提到了一个扩展,但是我应该利用内置的内容吗?

我希望有这样的功能,每次都不必去数据库:

public static int GetCountryId(string countryCode = "US")
{
    if (countryCode == string.Empty)
        countryCode = "US"; // Assume US
    return db.Country.Where
        (
            p => p.CountryCode == countryCode
        ).Select(p => p.CountryId).First();
}

更新

我现在正在考虑使用泛型来在内存中保存一些查找列表的GetCached函数。像这样的事情:

public class Lookups
{
    private static MkpContext db = new MkpContext();

    public static IEnumerable GetCached(CachedLists list)
    {
        ObjectCache cache = MemoryCache.Default;
        var listOut = cache[list.ToString()] as IEnumerable;
        if (listOut != null) return listOut;

        switch (list)
        {
            case CachedLists.Countries:
                cache.Set
                (
                    list.ToString(),
                    db.Country.ToList(),
                    new DateTimeOffset(DateTime.Now,new TimeSpan(1,0,0,0))
                );
                break;
            default:
                return null;
        }

        listOut = cache[list.ToString()] as IEnumerable;
        return listOut;
    }

}    

public enum CachedLists
{
    Countries,
}

但是上面的解决方案会给我留下一个un-typed Enumerable。我希望能够以某种方式指定类型,或者更好地进行某种扩展。

4 个答案:

答案 0 :(得分:1)

有很多选项,但是如果用户主要查询相同的几个国家/地区代码,这里有一种方法可以正常运行:

创建一个MemoryCache实例,用作您班级上的静态私有只读字段。在您的方法中,如果有一个具有给定countryCode的密钥项,请尝试从此缓存中获取缓存项。如果不存在,请在返回之前进行数据库查询并将结果添加到缓存中。

顺便说一下,你链接的文章中的方法可能不是一个很好的方法:只有你已经在你正在处理的特定数据库上下文实例中获得数据时才会有所帮助,通常它是最适合你的背景是短暂的。将表达式编译到函数中然后针对上下文缓存的每个实体运行该函数也只是CPU密集型,只是为了找出该项是否存在。如果你发现它不在那里,你还是要回到数据库,那你就浪费了时间和资源。

答案 1 :(得分:1)

您提出的方法通常是我返回缓存数据的方法。

然而,数据是作为对象缓存的,因此您必须将其作为您期望的对象返回。

让我们假设您的国家/地区类表示为:

public class Country
{
    #region Constructors

    public Country(string code, string name)
    {
        this.Code = code;
        this.Name = name;
    }

    #endregion

    #region Properties

    public string Name { get; set; }
    public string Code { get; set; }

    #endregion
}

我们需要做的是通过给定的键查找我们的缓存,如果存在则返回缓存,否则从数据库中获取 - 与您所做的逻辑相同。

区别就在这里var listOut = cache[list.ToString()] as IEnumerable;

您想检查该键 is 的类型 Country

的值是否

如果我们定义一个 GetCache 方法如下:

    static object GetCache(string cacheKey)
    {
        if (Cache[cacheKey] is object cachedResult)
        {
            return cachedResult;
        }

        return null;
    }

为了返回 List<Country> 我们需要做的是

        if (GetCache(cacheKey) is List<Country> cachedData)
        {
            return cachedData;
        }

现在我们有一个 List<Country>

而不是对象列表

我制作了一个简单的控制台应用程序来显示结果 - 希望对您有所帮助:

namespace ConsoleApp3
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Runtime.Caching;

    #endregion

    class Program
    {
        #region Fields

        private static readonly ObjectCache Cache = MemoryCache.Default;

        #endregion

        static void Main(string[] args)
        {
            //simulates app life span
            for (int i = 0; i < 5; i++)
            {
                var countries = GetData();
                foreach (var country in countries)
                {
                    Console.WriteLine(country.Name);
                }

                Console.ReadLine();
            }
        }

        static List<Country> GetData()
        {
            string cacheKey = "Country-Lookup";

            if (GetCache(cacheKey) is List<Country> cachedData)
            {
                return cachedData;
            }

            // otherwise do some logic stuff and get from DB

            //db data simulation
            List<Country> coutries = new List<Country>
            {
                new Country("IT", "Italy"),
                new Country("UK", "United Kindom"),
                new Country("US", "United States")
            };

            //add to cache
            AddToCache(cacheKey, coutries);

            return coutries;
        }

        static object GetCache(string cacheKey)
        {
            if (Cache[cacheKey] is object cachedResult)
            {
                return cachedResult;
            }

            return null;
        }

        static void AddToCache(string cacheKey, object dataToCache)
        {
            if (dataToCache == null)
            {
                return;
            }

            Cache.Set(cacheKey, dataToCache, DateTimeOffset.Now.AddMinutes(1));
        }
    }
}

答案 2 :(得分:1)

您可以使用 EntityFramework Plus。

public static int GetCountryId(string countryCode = "US")
{
    return db.Country.Where
        (
            p => p.CountryCode == countryCode
        ).Select(p => p.CountryId).First();
}

可能会变成……

public static int GetCountryId(string countryCode = "US")
{
    using var context = new Context();
    var allCountries = context.Country
                    .FromCache({cachePolicy})
                    .ToDictionary(x => x.CountryCode);
    return allCountries[countryCode];
}

答案 3 :(得分:0)

对于诸如将国家/地区代码映射到 countryId 之类的基本操作,我建议保持简单并使用字典。如果这种情况发生在 Web 应用程序中,您将需要使用 ConcurrentDictionary 来处理命中代码的多个线程,否则普通字典就可以了。

您还可以编写一些代码,在应用启动时填充字典,让用户体验更加快捷。

static ConcurrentDictionary<string, int> countryLookup = new ConcurrentDictionary<string, int>();

public static int GetCountryId(string countryCode = "US")
{
    if (countryCode == string.Empty)
        countryCode = "US"; // Assume US

    if (countryLookup.TryGetValue(countryCode, out int countryId))
        return countryId;

    var countryId = db.Country
        .First(p => p.CountryCode == countryCode)
        .CountryId;

    countryLookup.TryAdd(countryCode, countryId);
    return countryId;
}