建立数据库连接的最佳方法是什么(静态,抽象,按请求,......)?

时间:2013-04-09 12:58:14

标签: c# entity-framework design-patterns

我使用了很多模型连接到db,在我上一个与C#& amp;实体框架,我创建了数据库连接的静态类,但我有打开和关闭连接的问题,当超过10-15个请求聚集在一起时给我错误,我解决了它与更改连接到数据库的方法我现在连接每个请求并删除了所有静态方法和类。

现在我想知道,

建立连接的最佳模式是什么?

  1. 我应该在每次查询后关闭它并在使用之前打开它或者......?
  2. 静态类中的连接是很好的模型(我不需要 每次都创造它??
  3. 这个问题是否有良好的设计模式?
  4. 所有这些都是针对同一个问题的最佳方法 建立数据库连接(静态,抽象,每个请求,......)?
  5. 例如我在短信发件人网页面板上工作,我应该每秒发送100K短信,这些短信与其他人收集并制作一个包,每个包有1~20短信然后我需要每秒发送5K~100K包,当我发送包时,我应该执行以下步骤:

    1. 将单个短信更新为已发送或未发送
    2. 如果在useraccounts表中提交用户余额减少,则更新用户余额
    3. 更新用户表中的短信发送次数
    4. 更新手机号码表中的短信发送次数
    5. 更新发件人号码表中的短信发送次数
    6. 更新包表中已发送和失败的短信的包
    7. 更新程序包,了解程序包如何在程序包表中发送此程序包
    8. 更新线程表,了解通过此步骤发送多少短信以及失败的数量
    9. 在AccountDocument表格中为此交易添加帐户文档
    10. 所有其他步骤,如日志,用户界面和监视小部件,应该这样做,我需要数据库连接来完成每一笔交易。

      现在,连接数据库的最佳模型是什么?通过人工请求或线程请求或每次交易..

3 个答案:

答案 0 :(得分:8)

回答你的问题:

  1. 关闭它。 .NET为您提供连接池。

  2. 创建它。每次使用using(Connection conn = new ....) - 这样,您将充分利用.NET池机制。

  3. 您可以使用.NET ThreadPool(或您自己的自定义),定义ThreadPool并行使用10个线程并一个接一个地排队工作项。这样,在同一时间内不会再使用10个连接+它可能会更快地工作。 有关Custom ThreadPools的更多信息:Custom ThreadPool Implementation

  4. 每个实例。


  5. 以下是我对架构的建议:

    1. 为要发送的待处理短信创建数据库表(队列)。

    2. 每一行都包含短信所需的所有信息+当前状态。

    3. 创建一个工作进程,也许是一个Windows服务,它会不断地对这个表进行采样 - 比方说,每5秒钟。它会选择状态='待发送'的TOP~20短信(应该表示为int)。并将状态更新为“发送”

    4. 每个短信将在Windows服务端使用自定义线程池发送出去。

    5. 在流程结束时,所有已处理的短信状态将使用CTE更新为“已完成”(公用表表达式 - 您可以发送一个cte,其中包含刚刚处理过的所有短信行ID对“完成”状态进行“批量更新”。

    6. 您可以使状态更新存储过程与'getpending'相同。这样,您可以选择无需锁定的更新,并使数据库更快地运行。

    7. 这样,你可以运行不止一个处理器服务(但是你必须松开那个nolock)。

    8. 请记住避免尽可能多的锁定。

      顺便说一句,这也很好,因为您只需在待处理的SMS表中添加一行即可从系统中的任何位置发送短信。

      还有一件事,我不建议使用实体框架,因为它有太多的内容。这种任务所需要的只是简单地调用3-4个存储过程,就是这样。也许看看Dapper-dot-NET - 它是一个非常轻量级的MicroDal框架,在大多数情况下比EF(实体框架)的工作速度快10倍以上

答案 1 :(得分:7)

1. Should i close it after every query?

.Net为你这样做,让它处理它,这是一个垃圾收集器任务。所以不要手动处理你的对象,这是Jon Skeet的一个很好的答案:https://stackoverflow.com/a/1998600/544283。但是,您可以使用using(IDisposable){ }语句强制GC执行此操作。这是一篇关于资源重新分配的好文章:http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

2. A connection in static class is good?

从不使数据上下文成为静态!数据上下文线程安全或并发安全。

3. Is there a good design pattern for this problem?

正如Belogix所提到的,依赖注入和工作单元模式很棒,实际上实体框架 是一个工作单元本身。 DI和UoW有点被高估了,如果你第一次处理IoC容器就不容易实现,如果你走这条路我会推荐Ninject。另一件事是如果你不打算进行测试,你真的不需要DI,这些模式的真实性就是解耦,所以你可以毫无汗水地进行测试和模拟。

简而言之:如果您要对代码进行测试,请选择这些模式。如果没有,我将为您提供一个示例,说明如何在您喜欢的服务之间共享数据上下文。这是你第四个问题的答案。

4. What is the best method for making database connection (static, per request)?

您的上下文服务:

public class FooContextService {
    private readonly FooContext _ctx;

    public FooContext Context { get { return _ctx; } }

    public FooContextService() {
        _ctx = new FooContext();
    }
}

其他服务:

public class UnicornService {
    private readonly FooContext _ctx;

    public UnicornService(FooContextService contextService) {
        if (contextService == null)
            throw new ArgumentNullException("contextService");

        _ctx = contextService.Context;
    }

    public ICollection<Unicorn> GetList() {
        return _ctx.Unicorns.ToList();
    }
}

public class DragonService {
    private readonly FooContext _ctx;

    public DragonService(FooContextService contextService) {
        if (contextService == null)
            throw new ArgumentNullException("contextService");

        _ctx = contextService.Context;
    }

    public ICollection<Dragon> GetList() {
        return _ctx.Dragons.ToList();
    }
}

控制器:

public class FantasyController : Controller {
    private readonly FooContextService _contextService = new FooContextService();

    private readonly UnicornService _unicornService;
    private readonly DragonService _dragonService;

    public FantasyController() {
        _unicornService = new UnicornService(_contextService);
        _dragonService = new DragonService(_contextService);
    }

    // Controller actions
}

第二个想法(几乎是编辑): 如果您需要上下文不为您的实体创建代理,因此也没有延迟加载,您可以按如下方式重载上下文服务:

public class FooContextService {
    private readonly FooContext _ctx;

    public FooContext Context { get { return _ctx; } }

    public FooContextService() : this(true) { }

    public FooContextService(bool proxyCreationEnabled) {
        _ctx = new FooContext();
        _ctx.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
    }
}

注:

  • 如果您将代理创建设置为false,则会延迟加载。
  • 如果您有api控制器,想要处理任何完整的对象图。

编辑:

首先阅读一些内容:

完成这项工作:

(_context as IObjectContextAdapter).ObjectContext.Connection.Open();

这是一篇关于Managing Connections and Transactions的精彩文章。

实体框架通过Connection属性公开EntityConnection。读为:public sealed class EntityConnection : DbConnection

管理连接的注意事项:(取自上一个链接)

  • 如果在操作之前尚未打开连接,则对象上下文将打开该连接。如果对象上下文在操作期间打开连接,则它将始终在操作完成时关闭连接。
  • 如果手动打开连接,则对象上下文不会将其关闭。拨打关闭处理将关闭连接。
  • 如果对象上下文创建了连接,则在处理上下文时将始终处理连接。
  • 在长时间运行的对象上下文中,必须确保在不再需要上下文时处置该上下文。

希望它有所帮助。

答案 2 :(得分:5)

我认为每个请求都是最好的。使用线程安全的连接池并使连接范围与工作单元重合。让负责事务行为和工作单元的服务检查连接,使用它,并在提交或回滚工作单元时将其返回到池中。

更新:

提交状态更新10-12秒?你做错了什么。您提出的问题不足以提供合适的答案。

Daily NASDAQ volume是1.3B交易,每天工作8小时,每秒约45,000笔交易。您的交易量是纳斯达克的2倍。如果您尝试使用一台机器,我会说纳斯达克正在使用多台服务器。

我也想知道你是否可以在没有使用ACID更新状态的情况下做到这一点。毕竟,Starbucks doesn't use two-phase commit。也许更好的解决方案是使用带有阻塞队列的生产者/消费者模式,以便在发送后可以更新这些状态。