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

Meh*_*neh 12 c# design-patterns entity-framework

我使用了很多模型连接到db,在我上一个项目中,我使用C#和实体框架,我为db连接创建了静态类但是我有打开和关闭连接的问题,当我超过10-15时给我错误请求汇集在一起​​,我通过改变连接到db的方法来解决它,我现在每个请求连接并删除所有静态方法和类.

现在我想知道,

什么是连接的最佳模型?

  1. 我应该在每次查询后关闭它并在使用之前将其打开或......
  2. 静态类中的连接是一个很好的模型(我不需要每次都创建它)?
  3. 这个问题有一个好的设计模式吗?
  4. 所有这些都是针对同一个问题建立数据库连接的最佳方法是什么(静态,抽象,按请求,......)?

例如,我在短信发送器网络面板上工作,我应该每秒发送100K短信,这些短信与其他人一起收集,并制作一个包,每个包有1~20短信,然后我需要每秒发送5K~100K包,当我发送一个包我应该做这些步骤:

  1. 将单个短信更新为已发送或未发送
  2. 如果在useraccounts表中交付减少用户余额,则更新用户余额
  3. 更新用户表中的短信发送次数
  4. 更新移动号码表中的短信发送次数
  5. 更新发送方号码表中的短信发送计数
  6. 更新包表中已发送和失败的短信的包
  7. 更新程序包,了解线程如何在包表中发送此包
  8. 更新线程表,了解有多少短信通过此步骤发送它以及失败的数量
  9. 在AccountDocument表中为此事务添加帐户文档

所有步骤和许多其他事情,如日志,用户界面和监视小部件,应该这样做,我需要数据库连接来完成每一个这样的事务.

现在,什么是连接数据库的最佳模型?通过人工请求或线程请求或每个单独的事务..

Uri*_*son 8

你的问题的答案:

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

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

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

  4. 每个实例.


这是我对架构的建议:

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

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

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

  4. 每个sms将使用Windows服务端的自定义线程池发送出去.

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

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

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

记得要避免尽可能多的锁定.

顺便说一句,这也很好,因为您可以通过简单地向挂起的SMS表添加一行来从系统中的任何位置发送SMS.

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


Est*_*ban 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

其他服务:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器:

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
}
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:

  • 如果将代理创建设置为false,则不会启用延迟加载.
  • 如果你有API控制器,你希望处理任何完全成熟的对象图.

编辑:

一些先阅读:

完成这件事:

(_context as IObjectContextAdapter).ObjectContext.Connection.Open();
Run Code Online (Sandbox Code Playgroud)

这是一篇关于管理连接和事务的好文章.

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

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

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

希望能帮助到你.


duf*_*ymo 5

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

更新:

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

每日纳斯达克交易为1.3B,每天8小时的交易量为每秒约45,000笔交易.您的交易量是纳斯达克的2倍.如果你想用一台机器来做,我会说NASDAQ正在使用多台服务器.

我也想知道你是否可以在没有使用ACID更新状态的情况下做到这一点.毕竟,星巴克不使用两阶段提交.也许更好的解决方案是使用带有阻塞队列的生产者/消费者模式,以便在发送后可以更新这些状态.