有没有更好的方法在静态类中使用特定于请求的变量?

时间:2014-07-07 01:12:29

标签: c# linq entity-framework static

我正在构建LINQ扩展以通过EF简化数据库访问,并且该流程的一部分是将数据实体映射到业务实体。

我使用Dictionary<string, int>来决定要包含的导航属性以及深度。

示例:

public static class LinqExtensions
{
    private static readonly Dictionary<string, int> Dictionary = new Dictionary<string, int>();

    /// <summary>
    /// Adds the navigational property identified by value to be included in the query and entity mapping, recursing a maximum of depth times.
    /// </summary>
    /// <param name="value">Navigational Property to add</param>
    /// <param name="depth">Desired recursion depth</param>
    public static TSource With<TSource>(this TSource source, string value, int depth = 0)
    {
        Dictionary.Add(value, depth);
        return source;
    }

    /// <summary>
    /// Clears the navigational property dictionary
    /// </summary>
    public static void Reset()
    {
        Dictionary.Clear();
    }

    /// <summary>
    /// Builds and executes a query, dynamically including desired navigational properties in a asynchronous fashion.
    /// The result is then mapped to the provided TResult business entity and returned as a list. 
    /// </summary>
    /// <returns>Null or a list of mapped domain Entities</returns>
    public static async Task<IEnumerable<TResult>> BuildQueryAsync<TSource, TResult>(this IQueryable<TSource> dbEntity) where TResult : class, new()
    {
        var query = dbEntity;
        var localDictionary = new Dictionary<string, int>(Dictionary);
        Reset();

        foreach (var i in localDictionary)
        {
            query = query.Include(i.Key);
        }
        List<TSource> result = await (from entity in query select entity).ToListAsync();

        return Equals(result, default(TSource)) ? null : result.Select(u => u.BuildEntity(new TResult(), localDictionary));
    }

    /// <summary>
    /// Maps values from sourceEntity to targetEntity, recursing into properties defined in localDictionary.
    /// </summary>
    public static TTarget BuildEntity<TSource, TTarget>(this TSource sourceEntity, TTarget targetEntity, Dictionary<string, int> localDictionary)
    {
        return (TTarget)targetEntity.InjectFrom(new SinglePropertyDepthInjection(localDictionary), sourceEntity);
    }
}

这让我可以访问我的存储库和服务中的内容,如下所示:

    public override async Task<IEnumerable<User>> GetAllAsync()
    {
        return await _context.Users.With("Messages", 1).With("Notifications", 2).BuildQueryAsync<Data.Entities.User, User>();
    }

现在我很清楚这是不可行的,因为所有请求都共享静态属性。

我知道我可以轻松地添加一个字典作为方法参数,并解决它:

    public override async Task<IEnumerable<User>> GetAllAsync()
    {
        var dict = new Dictionary<string, int>();

        dict.Add("Messages", 1);
        dict.Add("Notifications", 2);

        return await _context.Users.BuildQueryAsync<Data.Entities.User, User>(dict);
    }

但我想知道是否有更优雅的解决方案,理想情况下将其作为LINQ查询的一部分。

我知道有HttpContext.Current,但由于涉及的方法是异步的,我不确定回到上下文线程的想法有多好。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

我认为CallContext可能就是你要找的东西。

与一次性图案相结合,这样的东西可以非常简单。

public class IncludeScope : IDisposable
{
    private const string CallContextKey = "IncludeScopKey";

    private object oldValue;

    public IncludeScope(IDictionary<string,int> values)
    {
        this.oldValue = CallContext.GetData(CallContextKey);
        this.Includes = new Dictionary<string,int>(values);
        CallContext.SetData(CallContextKey, this);
    }

    public Dictionary<string,int> Includes { get; private set; }

    public static IncludeScope Current {
        get { return CallContext.GetData(CallContextKey) as IncludeScope; }
    }

    private bool _disposed;

    protected virtual bool IsDisposed
    {
        get
        {
            return _disposed;
        }
    }

    ~IncludeScope()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed) {
            if (disposing) {
                CallContext.SetData(CallContextKey, oldValue);
            }
            _disposed = true;
        }
    }
}

范围可以这样声明。

using(var scope = new IncludeScope(new Dictionary<string,int>{{"Message",1}, {"Notifications",2}})){
    var query = await GetQueryAsync<User>();
    …
}

在使用范围内的任何方法调用中,可以像这样访问范围。

private static Task<IQueryable<T>> GetQueryAsync<T>() { 
    var baseQuery = context.Set<T>();
    foreach (var include in IncludeScope.Current.Includes) { 

    }
}