首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为nolock选择query,而不跟踪更改

为nolock选择query,而不跟踪更改
EN

Code Review用户
提问于 2016-06-24 08:34:10
回答 1查看 1.5K关注 0票数 3

我经常需要切换连接字符串和数据库(dev/prod),并且需要能够使用NOLOCK标志执行查询。

为此目的,我创建了一些实用程序,这些实用程序应该会使这一点变得更容易一些。

主要的类是QueryService,它封装了每个查询/数据库必须重复的enitre设置过程。我使用的大多数数据库都是只读的,因此我不需要更改跟踪,因此需要允许禁用它的属性。我用的是模型-首先对所有的人。

代码语言:javascript
复制
public abstract class QueryService<TDbContext> where TDbContext : DbContext
{
    protected QueryService(string connectionStringName, string environmentName = null)
    {
        if (string.IsNullOrEmpty(connectionStringName)) { throw new ArgumentNullException("connectionStringName"); }
        if (string.IsNullOrEmpty(environmentName)) { throw new ArgumentNullException("environmentName"); }

        ConnectionStringName = connectionStringName;
        EnvironmentName = environmentName;
        AutoDetectChangesEnabled = true;
    }
    public Action<string> Log { get; set; }

    public string ConnectionStringName { get; private set; }

    public string EnvironmentName { get; private set; }

    public bool AutoDetectChangesEnabled { get; set; }

    public TResult Execute<TResult>(Func<TDbContext, TResult> query, bool nolock = true)
    {
        if (query == null) { throw new ArgumentNullException("query"); }

        var connectionStringFullName = ConnectionStringName + (string.IsNullOrEmpty(EnvironmentName) ? string.Empty : "." + EnvironmentName);

        using (var context = DbContextFactory.Create<TDbContext>(connectionStringFullName))
        {
            context.Configuration.AutoDetectChangesEnabled = AutoDetectChangesEnabled;
            context.Database.Log = Log;
            return nolock ? context.AsNolock(query) : query(context);
        }
    }
}

它得到另外两个实用程序的支持。

DbContextFactory -它的任务是创建必须有接受连接字符串名称的构造函数的上下文。

代码语言:javascript
复制
public class DbContextFactory
{
    public static TDbContext Create<TDbContext>(string connectionStringName) 
        where TDbContext : DbContext
    {
        var dbContext = (TDbContext)Activator.CreateInstance(
            typeof(TDbContext), connectionStringName);
        return dbContext;
    }
}

以及提供nolock选项的DbContext的扩展。

代码语言:javascript
复制
public static class DbContextExtensions
{
    public static T AsNolock<TDbContext, T>(this TDbContext context, Func<TContext, T> query) 
        where TDbContext : DbContext
    {
        using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions 
        { 
            IsolationLevel = IsolationLevel.ReadUncommitted 
        }))
        {
            var result = query(context);
            scope.Complete();
            return result;
        }
    }
}

通常,我通过为特定模型的查询创建一个静态类来使用这个方法:

代码语言:javascript
复制
// theoretical foo-context for a foo-model
public class FooContext : DbContext
{
    public virtual ICollection<string> Strings { get; set; }
}

// query service for the foo-model
public class FooQueryService : QueryService<FooContext>
{
    public FooQueryService(string environmentName) : base("Foo", environmentName) { }
}

// queries for the foo-model
public static class FooQueries
{
    // in the real app the "bar" is not a magic-string but a setting
    private static readonly FooQueryService FooQueryService = 
        new FooQueryService("bar") 
        {
            AutoDetectChangesEnabled = false 
        };

    public static List<string> GetStrings()
    {
        return FooQueryService.Execute(ctx => ctx.Strings.ToList(), nolock: true);
    }
}

我还为在LINQPad中测试一些查询时创建了一个静态助手:

代码语言:javascript
复制
internal class DynamicQueryService<TDbContext> : QueryService<TDbContext> 
    where TDbContext : DbContext
{
    public DynamicQueryService(string connectionStringName, string environmentName)
        : base(connectionStringName, environmentName) { }
}

public static class SelectQuery
{
    public static TResult Execute<TDbContext, TResult>(
        string connectionStringName,
        string environmentName,
        Func<TDbContext, TResult> query,
        bool nolock = true,
        bool autoDetectChangesEnabled = true,
        Action<string> log = null) where TDbContext : DbContext
    {
        return new DynamicQueryService<TDbContext>(connectionStringName, environmentName)
        {
            AutoDetectChangesEnabled = autoDetectChangesEnabled,
            Log = log
        }.Execute(query, nolock);
    }
}

在那里,我只需调用Execute方法并快速更改连接字符串或环境:

代码语言:javascript
复制
var result = SelectQuery.Execute<FooContext>(
    "foo",
    "dev",
    ctx => ctx.Strings.ToList(),
    nolock: true,
    autoDetectChangesEnabled: false,
    log: null);
EN

回答 1

Code Review用户

回答已采纳

发布于 2016-06-24 19:43:42

好的,让我们尝试以一种可靠的方式来处理它(一切都是不可变的,线程是安全的):

代码语言:javascript
复制
public class FooContext : DbContext
{
    public virtual ICollection<string> Strings { get; set; }
}

public static class FooQueries
{
    static IConnection Connection => new Connection("connectionName", "environment")
        .Log(Console.WriteLine)
        .NoTracking();

    public static IList<string> GetStrings() => Connection
        .Query((FooContext c) => c.Strings.ToList())
        .WithNoLock()
        .Execute();
}

在我们使用的地方:

代码语言:javascript
复制
public interface IConnection
{
    T ToContext<T>() where T : DbContext;
}

public interface IQuery<TResult>
{
    TResult Execute();
}

具有扩展类:

代码语言:javascript
复制
public static class Queries
{
    public static IConnection Log(this IConnection connection, Action<string> log) =>
        new ConfiguringConnection(connection, c => c.Database.Log = log);

    public static IConnection NoTracking(this IConnection connection) =>
        new ConfiguringConnection(connection, c => c.Configuration.AutoDetectChangesEnabled = false);

    public static IQuery<TResult> Query<TContext, TResult>(this IConnection connection, Func<TContext, TResult> selector)
        where TContext : DbContext =>
        new Query<TContext, TResult>(connection, selector);

    public static IQuery<TResult> WithNoLock<TResult>(this IQuery<TResult> query) =>
        new NoLockQuery<TResult>(query);
}

和:

代码语言:javascript
复制
public class Connection : IConnection
{
    public Connection(string name, string environment = null)
    {
        Name = name;
        Environment = environment;
    }

    public T ToContext<T>()
        where T : DbContext =>
        (T)Activator.CreateInstance(typeof(T), ToString());

    public override string ToString() =>
        string.IsNullOrEmpty(Environment) ? Name : Name + "." + Environment;

    string Name { get; }
    string Environment { get; }
}

和:

代码语言:javascript
复制
class ConfiguringConnection : IConnection
{
    public ConfiguringConnection(IConnection parent, Action<DbContext> setup)
    {
        Parent = parent;
        Setup = setup;
    }

    public T ToContext<T>() where T : DbContext
    {
        var context = Parent.ToContext<T>();
        Setup(context);
        return context;
    }

    protected IConnection Parent { get; }
    protected Action<DbContext> Setup { get; }
}

和:

代码语言:javascript
复制
class NoLockQuery<TResult> : IQuery<TResult>
{
    public NoLockQuery(IQuery<TResult> parent)
    {
        Parent = parent;
    }

    public TResult Execute()
    {
        using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions
        {
            IsolationLevel = IsolationLevel.ReadUncommitted
        }))
        {
            var result = Parent.Execute();
            scope.Complete();
            return result;
        }
    }

    IQuery<TResult> Parent { get; }
}

和:

代码语言:javascript
复制
class Query<TContext, TResult> : IQuery<TResult> 
    where TContext : DbContext
{
    public Query(IConnection connection, Func<TContext, TResult> selector)
    {
        Connection = connection;
        Selector = selector;
    }

    public TResult Execute() => Selector(Connection.ToContext<TContext>());
    IConnection Connection { get; }
    Func<TContext, TResult> Selector { get; }
}

我希望所有这些东西都能用

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/132931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档