使用嵌套字典编写此类条目的最佳、更实用的方法是什么?使用哪种设计模式? C#

问题描述 投票:0回答:2

数据库中有大量数据,需要使用c#集合来产生统计数据(找到应用程序的每个用户每天每个操作的平均次数)。我认为,使用词典是有必要的:

var dict = new Dictionary<long?, Dictionary<DateTime, Dictionary<OperationsGroupType, int>>>();

请建议更实用的写法。因为看起来很奇怪。谢谢你

我写了一个函数:

public void D()
        {
            var dict = new Dictionary<long?, Dictionary<DateTime, Dictionary<OperationsGroupType, int>>>();
            int pageNumber = 0;
            int pageSize = 5;
            int pageCount = 1;

            while (pageNumber < pageCount)
            {
                int count;
                foreach (OperationData op in OperationService.GetPage(pageNumber, pageSize, out count))
                    if(op.PerformedBy.HasValue)
                        if(op.PerformedDate.HasValue)
                            if (dict.ContainsKey(op.PerformedBy))
                                if (dict[op.PerformedBy].ContainsKey(op.PerformedDate.Value.Date.Date))
                                    if (dict[op.PerformedBy][op.PerformedDate.Value.Date.Date.Date.Date].ContainsKey(op.Type)) dict[op.PerformedBy][op.PerformedDate.Value.Date.Date.Date.Date][op.Type]++;
                                    else dict[op.PerformedBy][op.PerformedDate.Value.Date.Date.Date.Date].Add(op.Type, 1);
                                else dict[op.PerformedBy].Add(op.PerformedDate.Value.Date.Date.Date.Date, new Dictionary<OperationsGroupType, int> { { op.Type, 1 } });
                            else dict.Add(op.PerformedBy, new Dictionary<DateTime, Dictionary<OperationsGroupType, int>> { { op.PerformedDate.Value.Date.Date.Date.Date, new Dictionary<OperationsGroupType, int> { { op.Type, 1 } } } });

                pageCount = (count - 1) / pageSize + 1;
                pageNumber++;
            }

            foreach (var item in dict)
            {
                var opDateDict = new Dictionary<DateTime, int>();
                foreach (var operDate in item.Value) opDateDict.Add(operDate.Key, operDate.Value.Sum(count => count.Value));

                SystemLogger.Instance.WriteErrorTrace(String.Format("Average number of user operations {0} per day: {1}\n", item.Key, opDateDict.Values.Sum() / opDateDict.Count));
            }
        }
OperationsGroupType - this enum

请告诉我如何用更实用的设计来代替字典? 哪种模式最适合解决这个问题?

c# generics collections
2个回答
1
投票

很难说出什么是最好的或最实用的 - 那是因为你没有真正定义“最好”或“实用”的含义。

我将把它们定义为最少的代码和最少的重复。

首先,我创建了这些扩展方法:

public static class Ex
{
    public static R Ensure<T, R>(this Dictionary<T, R> @this, T key) where R : new
    {
        if (@this.ContainsKey(key))
            return @this[key];
        else
        {
            var r = new R();
            @this[key] = r;
            return r;
        }
    }

    public static R Ensure<T, R>(this Dictionary<T, R> @this, T key, Func<R> factory)
    {
        if (@this.ContainsKey(key))
            return @this[key];
        else
        {
            var r = factory();
            @this[key] = r;
            return r;
        }
    }
}

有了这些,我可以像这样重写你的代码:

foreach (OperationData op in OperationService.GetPage(pageNumber, pageSize, out count))
{
    if (op.PerformedBy.HasValue)
        if (op.PerformedDate.HasValue)
        {
            dict.Ensure(op.PerformedBy).Ensure(op.PerformedDate.Value.Date).Ensure(op.Type, () => 0);
            dict[op.PerformedBy][op.PerformedDate.Value.Date][op.Type]++;
        }
}

0
投票

有一个不同的(更新的)选项吗,即我们可以使用嵌套字典来代替

只读记录基于结构的键

即考虑到嵌套字典的想法,例如:

var dictsNested = new Dictionary<long?, Dictionary<DateTime, Dictionary<OperationsGroupType, int>>>();

..我们可以创建一个

readonly record struct
将所有键组合成一个:

readonly record struct CombinedKeys(long? SomeId, DateTime SomeTimestamp, OperationsGroupType SomeEnumValue);

然后我们可以使用单个字典:

var myDict = new Dictionary<CombinedKeys, int>();

我相信这将提供快速查找(由于编译器合成了 GetHashCode() 和 Equals(),并消除了您想要避免的复杂性。

多线程

如果您正在处理多线程环境,您可以使用 ConcurrentDictionary<>...

var myDict = new ConcurrentDictionary<CombinedKeys, int>();

...与这个想法相结合,嵌套方法的许多问题都被删除/减少,例如

if (dictsNested.TryGetValue(someNullableLong, out Dictionary<DateTime, Dictionary<OperationsGroupType, int>> firstInnerDic))
{
    //diff. thread tries to remove keyValue pair for key someNullableLong, or changes the value for the key  
    if(firstInnerDic.TryGetValue(someDateTime, out Dictionary<OperationsGroupType, int> secondInnerDic))
    {
       //so on... what happens if another thread has changed the outer dictionary value? i.e. when are dealing with concurrency / race conditions... nested dictionaries can quickly become unmanageable? 
    }
}

更多信息

记录结构为我们提供快速查找的方式,即应该比通常的 C# 用户定义的结构更快(使用 GetHashCode 和 Equals 的反射,因此不太适合作为高性能字典使用的键)

有关

一般记录的信息

© www.soinside.com 2019 - 2024. All rights reserved.