首先在windows上安装好Redis,RabbitMQ
Redis-cli使用示例
ModelContext.cs代码:
public class ModelContext : DbContext
{
public ModelContext()
: base(
"name=default")
{
}
public virtual DbSet<Person> Person {
get;
set; }
}
public class Person
{
public int Id {
get;
set; }
public string Id2 {
get;
set; }
public string Name {
get;
set; }
}
在 Package Manager Console 下运行命令 Enable-Migrations 这个命令将在项目下创建文件夹 Migrations
The Configuration class 这个类允许你去配置如何迁移,对于本文将使用默认的配置(在本文中因为只有一个 Context, Enable-Migrations 将自动对 context type 作出适配); An InitialCreate migration (本文为201702220232375_20170222.cs)这个迁移之所以存在是因为我们之前用 Code First 创建了数据库, 在启用迁移前,scaffolded migration 里的代码表示在数据库中已经创建的对象,本文中即为表 Person(列 Id 和 Name). 文件名包含一个 timestamp 以便排序(如果之前数据库没有被创建,那么 InitialCreate migration 将不会被创建,相反,当我们第一次调用 Add-Migration 的时候所有表都将归集到一个新的 migration 中)
多个实体锁定同一数据库
当使用 EF6 之前的版本时,只会有一个 Code First Model 被用来生成/管理数据库的 Schema, 这将导致每个数据库只会有一张 __MigrationsHistory 表,从而无法辨别实体与模型的对应关系。
从 EF6 开始,Configuration 类将会包含一个 ContextKey 属性,它将作为每一个 Code First Model 的唯一标识符, __MigrationsHistory 表中一个相应地的列允许来自多个模型(multiple models)的实体共享表(entries),默认情况下这个属性被设置成 context 的完全限定名。
定制化迁移
在 Package Manager Console 中运行命令 Add-Migration XXXXXXXXX 生成的迁移如下
public partial class _20170222 : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.People",
c =>
new
{
Id = c.Int(nullable:
false, identity:
true),
Id2 = c.String(),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropTable(
"dbo.People");
}
}
Configuration.cs代码:
internal sealed class Configuration : DbMigrationsConfiguration<EF.ModelContext>
{
public Configuration()
{
AutomaticMigrationsEnabled =
false;
}
protected override void Seed(EF.ModelContext context)
{
}
}
我们对迁移做些更改:
以下是本项目无关的其他示例:
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostClass : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Posts",
c =>
new
{
PostId = c.Int(nullable:
false, identity:
true),
Title = c.String(maxLength:
200),
Content = c.String(),
BlogId = c.Int(nullable:
false),
})
.PrimaryKey(t => t.PostId)
.ForeignKey(
"dbo.Blogs", t => t.BlogId, cascadeDelete:
true)
.Index(t => t.BlogId)
.Index(p => p.Title, unique:
true);
AddColumn(
"dbo.Blogs",
"Rating", c => c.Int(nullable:
false, defaultValue:
3));
}
public override void Down()
{
DropIndex(
"dbo.Posts",
new[] {
"Title" });
DropForeignKey(
"dbo.Posts",
"BlogId",
"dbo.Blogs");
DropIndex(
"dbo.Posts",
new[] {
"BlogId" });
DropColumn(
"dbo.Blogs",
"Rating");
DropTable(
"dbo.Posts");
}
}
}
在 Package Manager Console 中运行命令 Update-Database –Verbose
消费者端,用来把消息队列里的数据写入数据库
MqHelper.cs代码:
public class MqHelper
{
private static IConnection _connection;
public static IConnection
GetConnection()
{
if (_connection !=
null)
return _connection;
_connection = GetNewConnection();
return _connection;
}
public static IConnection
GetNewConnection()
{
var factory =
new ConnectionFactory()
{
HostName =
"localhost",
UserName =
"guest",
Password =
"guest",
};
_connection = factory.CreateConnection();
return _connection;
}
}
Program.cs代码:
internal class Program
{
private static void Main(
string[] args)
{
using (
var channel = MqHelper.GetConnection().CreateModel())
{
channel.QueueDeclare(
"NET",
true,
false,
false,
null);
var consumber =
new EventingBasicConsumer(channel);
channel.BasicQos(
0,
1,
false);
consumber.Received += (sender, e) =>
{
try
{
var user = JsonConvert.DeserializeObject<User>(Encoding.UTF8.GetString(e.Body));
var flag = RedisHelper.GetRedisClient().Incr(user.Id.ToString());
if (flag ==
1)
{
Console.WriteLine(
string.Format(
"{0}标识为{1} {2}", user.Id, flag, user.Name));
var dbContext =
new ModelContext();
dbContext.Person.Add(
new Person() {Id2 = user.Id.ToString(), Name = user.Name});
Task ts = dbContext.SaveChangesAsync();
ts.Wait();
RedisHelper.GetRedisClient().Incr(
string.Format(
"{0}入库", user.Id.ToString()));
Console.WriteLine(
"入库成功");
}
channel.BasicAck(e.DeliveryTag,
false);
}
catch (Exception ex)
{
File.AppendAllText(
string.Format(
"{0}/bin/log.txt", System.AppDomain.CurrentDomain.BaseDirectory), ex.Message);
}
};
Console.WriteLine(
"开始工作");
channel.BasicConsume(
"NET",
false, consumber);
Console.ReadKey();
}
}
}
模拟并发的MVC网站,写入队列
HomeController.cs代码
public class HomeController : Controller
{
public ActionResult
Index()
{
return View();
}
[HttpPost]
public ActionResult
GrabSingle(User user)
{
MqPublish.AddQueue(user);
return Json(
new { Status =
"OK" });
}
[HttpGet]
public async Task<ActionResult>
GetCount()
{
using (
var dbcontext =
new ModelContext())
{
return Json(
new { Count =
await dbcontext.Person.CountAsync() }, JsonRequestBehavior.AllowGet);
}
}
}
MqPublish.cs类
public class MqPublish
{
public const string QueueName =
"NET";
public static IList<User> UserList =
new List<User>();
public static void AddQueue(User user)
{
using (
var channel = MqHelper.GetNewConnection().CreateModel())
{
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(user));
channel.BasicPublish(String.Empty, QueueName,
null, bytes);
}
}
}
RedisHelper.cs代码:
public class RedisHelper
{
public static RedisClient
GetRedisClient()
{
return new RedisClient(
"127.0.0.1",
6379,
"123456");
}
}
Index.cshtml内容:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index
</title>
<link href="//cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet">
<script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script>
var uarray = [
{ Id: 1, Name: "小明" }, { Id: 2, Name: "张三" }, { Id: 3, Name: "李四" }, { Id: 4, Name: "王五" },
{ Id: 5, Name: "赵六" }, { Id: 6, Name: "钱八" }, { Id: 7, Name: "小红" }, { Id: 8, Name: "小紫" },
{ Id: 9, Name: "小蓝" }, { Id: 10, Name: "老王" }
];
function btnClick() {
var flag;
for (var i = 0; i < 1000; i++) {
flag = parseInt(Math.random() * (9 - 0 + 1) + 0);
$.ajax({
type: "post",
url: "/Home/GrabSingle",
data: uarray[flag]
});
}
}
function GetCount() {
$.ajax({
type: "get",
url: "/Home/GetCount",
dataType: "json",
success: function (data) {
alert(data.Count + "个");
}
});
}
</script>
</head>
<body>
<h2>.Net高并发解决思路
</h2>
<hr />
<button class="btn btn-primary" onclick="btnClick()">点击抢单
</button>
<button class="btn btn-primary" onclick="GetCount()">查看入库数量
</button>
</body>
</html>
Startup.cs代码:
using System;
using System.Threading.Tasks;
using Hangfire;
using Hangfire.MemoryStorage;
using Microsoft.Owin;
using NetHigh.RabbitMq;
using Owin;
[assembly: OwinStartup(
typeof(NetHigh.Startup))]
namespace NetHigh
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
GlobalConfiguration.Configuration.UseMemoryStorage();
app.UseHangfireServer();
app.UseHangfireDashboard(
"/hangfire");
}
}
}
Web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<connectionStrings>
<add name="default" connectionString="Server=.;User Id=sa;PassWord=123456;DataBase=Person" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
运行结果如图:
运行消费者端(控制台运用程序)