Meh*_*neh 12 c# design-patterns entity-framework
我使用了很多模型连接到db,在我上一个项目中,我使用C#和实体框架,我为db连接创建了静态类但是我有打开和关闭连接的问题,当我超过10-15时给我错误请求汇集在一起,我通过改变连接到db的方法来解决它,我现在每个请求连接并删除所有静态方法和类.
现在我想知道,
什么是连接的最佳模型?
例如,我在短信发送器网络面板上工作,我应该每秒发送100K短信,这些短信与其他人一起收集,并制作一个包,每个包有1~20短信,然后我需要每秒发送5K~100K包,当我发送一个包我应该做这些步骤:
所有步骤和许多其他事情,如日志,用户界面和监视小部件,应该这样做,我需要数据库连接来完成每一个这样的事务.
现在,什么是连接数据库的最佳模型?通过人工请求或线程请求或每个单独的事务..
你的问题的答案:
关闭它..NET为您提供连接池.
创造它.每次使用using(Connection conn = new ....) - 这样,您将充分利用.NET池机制.
你可以使用.NET ThreadPool(或你自己的自定义),定义ThreadPool并行使用10个线程并一个接一个地排队工作项.这样,在同一时间内不会再使用10个连接+它可能会更快地工作.有关Custom ThreadPools的更多信息:自定义ThreadPool实现
每个实例.
这是我对架构的建议:
为要发送的待处理SMS创建数据库表(队列).
每行将包含短信所需的所有信息+当前状态.
创建一个工作进程,也许是一个Windows服务,它会不断地对这个表进行采样 - 比方说,每5秒钟.它会选择状态='待发送'的TOP~20短信(应该表示为int).并将状态更新为"发送"
每个sms将使用Windows服务端的自定义线程池发送出去.
在流程结束时,所有已处理的短信状态将使用CTE更新为"已完成"(公用表表达式 - 您可以发送一个cte,其中包含刚刚进行'批量更新'的所有短信行ID到'完成'状态).
您可以使状态更新存储过程与'getpending'相同.这样,您可以选择无需锁定的更新,并使数据库更快地运行.
这样,你可以运行不止一个处理器服务(但是你必须松开nolock).
记得要避免尽可能多的锁定.
顺便说一句,这也很好,因为您可以通过简单地向挂起的SMS表添加一行来从系统中的任何位置发送SMS.
还有一件事,我不建议使用实体框架,因为它有太多的内容.这种任务所需要的只是简单地调用3-4个存储过程,就是这样.也许看看Dapper-dot- NET--它是一个非常轻量级的MicroDal框架,在大多数情况下比EF(实体框架)的工作速度快10倍以上
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)
注意:
编辑:
一些先阅读:
完成这件事:
(_context as IObjectContextAdapter).ObjectContext.Connection.Open();
Run Code Online (Sandbox Code Playgroud)
这是一篇关于管理连接和事务的好文章.
实体框架通过Connection属性公开EntityConnection.阅读:public sealed class EntityConnection : DbConnection.
管理连接的注意事项:(取自上一个链接)
希望能帮助到你.
我认为每个请求都是最好的.使用线程安全的连接池并使连接范围与工作单元重合.让负责事务行为和工作单元的服务检查连接,使用它,并在提交或回滚工作单元时将其返回到池中.
更新:
提交状态更新需要10-12秒?你做错了什么.您提出的问题不足以提供合适的答案.
每日纳斯达克交易量为1.3B,每天8小时的交易量为每秒约45,000笔交易.您的交易量是纳斯达克的2倍.如果你想用一台机器来做,我会说NASDAQ正在使用多台服务器.
我也想知道你是否可以在没有使用ACID更新状态的情况下做到这一点.毕竟,星巴克不使用两阶段提交.也许更好的解决方案是使用带有阻塞队列的生产者/消费者模式,以便在发送后可以更新这些状态.