@@ -1,3 +1,6 @@ | |||
[![Build status](https://ci.appveyor.com/api/projects/status/4mpe0tbu7n126vyw?svg=true)](https://ci.appveyor.com/project/yuleyule66/cap) | |||
[![Build status](https://ci.appveyor.com/api/projects/status/4mpe0tbu7n126vyw/branch/master?svg=true)](https://ci.appveyor.com/project/yuleyule66/cap/branch/master) | |||
# cap | |||
A .net core middleware of eventually consistent in distributed architectures, now developing... | |||
@@ -3,44 +3,49 @@ using System.Data.SqlClient; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public static class ConnectionUtil | |||
{ | |||
private const string DatabaseVariable = "Cap_SqlServer_DatabaseName"; | |||
private const string ConnectionStringTemplateVariable = "Cap_SqlServer_ConnectionStringTemplate"; | |||
private const string MasterDatabaseName = "master"; | |||
private const string DefaultDatabaseName = @"DotNetCore.CAP.EntityFrameworkCore.Test"; | |||
private const string DefaultConnectionStringTemplate = @"Server=.\sqlexpress;Database={0};Trusted_Connection=True;"; | |||
public static string GetDatabaseName() | |||
{ | |||
return Environment.GetEnvironmentVariable(DatabaseVariable) ?? DefaultDatabaseName; | |||
} | |||
public static string GetMasterConnectionString() | |||
{ | |||
return string.Format(GetConnectionStringTemplate(), MasterDatabaseName); | |||
} | |||
public static string GetConnectionString() | |||
{ | |||
return string.Format(GetConnectionStringTemplate(), GetDatabaseName()); | |||
} | |||
private static string GetConnectionStringTemplate() | |||
{ | |||
return | |||
Environment.GetEnvironmentVariable(ConnectionStringTemplateVariable) ?? | |||
DefaultConnectionStringTemplate; | |||
} | |||
public static SqlConnection CreateConnection(string connectionString = null) | |||
{ | |||
connectionString = connectionString ?? GetConnectionString(); | |||
var connection = new SqlConnection(connectionString); | |||
connection.Open(); | |||
return connection; | |||
} | |||
} | |||
public static class ConnectionUtil | |||
{ | |||
private const string DatabaseVariable = "Cap_SqlServer_DatabaseName"; | |||
private const string ConnectionStringTemplateVariable = "Cap_SqlServer_ConnectionStringTemplate"; | |||
private const string MasterDatabaseName = "master"; | |||
private const string DefaultDatabaseName = @"DotNetCore.CAP.EntityFrameworkCore.Test"; | |||
//private const string DefaultConnectionStringTemplate = @"Server=.\sqlexpress;Database={0};Trusted_Connection=True;"; | |||
private const string DefaultConnectionStringTemplate = @"Server=192.168.2.206;Initial Catalog={0};User Id=sa;Password=123123;MultipleActiveResultSets=True"; | |||
public static string GetDatabaseName() | |||
{ | |||
return Environment.GetEnvironmentVariable(DatabaseVariable) ?? DefaultDatabaseName; | |||
} | |||
public static string GetMasterConnectionString() | |||
{ | |||
return string.Format(GetConnectionStringTemplate(), MasterDatabaseName); | |||
} | |||
public static string GetConnectionString() | |||
{ | |||
//if (Environment.GetEnvironmentVariable("ASPNETCore_Environment") == "Development") | |||
//{ | |||
// return "Server=192.168.2.206;Initial Catalog=Test2;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"; | |||
//} | |||
return string.Format(GetConnectionStringTemplate(), GetDatabaseName()); | |||
} | |||
private static string GetConnectionStringTemplate() | |||
{ | |||
return | |||
Environment.GetEnvironmentVariable(ConnectionStringTemplateVariable) ?? | |||
DefaultConnectionStringTemplate; | |||
} | |||
public static SqlConnection CreateConnection(string connectionString = null) | |||
{ | |||
connectionString = connectionString ?? GetConnectionString(); | |||
var connection = new SqlConnection(connectionString); | |||
connection.Open(); | |||
return connection; | |||
} | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
using System.Data; | |||
using System.Threading.Tasks; | |||
using Dapper; | |||
using Microsoft.EntityFrameworkCore; | |||
@@ -26,10 +27,10 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
using (CreateScope()) | |||
{ | |||
var context = GetService<CapDbContext>(); | |||
var context = GetService<TestDbContext>(); | |||
context.Database.EnsureDeleted(); | |||
context.Database.Migrate(); | |||
_sqlObjectInstalled = true; | |||
_sqlObjectInstalled = true; | |||
} | |||
} | |||
} | |||
@@ -38,7 +39,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
using (CreateScope()) | |||
{ | |||
var context = GetService<CapDbContext>(); | |||
var context = GetService<TestDbContext>(); | |||
var commands = new[] | |||
{ | |||
@@ -1,27 +0,0 @@ | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public static class DbUtil | |||
{ | |||
public static IServiceCollection ConfigureDbServices(string connectionString, IServiceCollection services = null) { | |||
return ConfigureDbServices<CapDbContext>(connectionString, services); | |||
} | |||
public static IServiceCollection ConfigureDbServices<TContext>(string connectionString, IServiceCollection services = null) where TContext : DbContext { | |||
if (services == null) { | |||
services = new ServiceCollection(); | |||
} | |||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||
services.AddDbContext<TContext>(options => options.UseSqlServer(connectionString)); | |||
return services; | |||
} | |||
public static TContext Create<TContext>(string connectionString) where TContext : DbContext { | |||
var serviceProvider = ConfigureDbServices<TContext>(connectionString).BuildServiceProvider(); | |||
return serviceProvider.GetRequiredService<TContext>(); | |||
} | |||
} | |||
} |
@@ -1,53 +0,0 @@ | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Infrastructure; | |||
using Microsoft.AspNetCore.Builder.Internal; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Xunit; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public class DefaultPocoTest : IClassFixture<ScratchDatabaseFixture> | |||
{ | |||
private readonly ApplicationBuilder _builder; | |||
public DefaultPocoTest(ScratchDatabaseFixture fixture) | |||
{ | |||
var services = new ServiceCollection(); | |||
services | |||
.AddDbContext<CapDbContext>(o => o.UseSqlServer(fixture.ConnectionString)) | |||
.AddConsistency() | |||
.AddEntityFrameworkStores<CapDbContext>(); | |||
services.AddLogging(); | |||
var provider = services.BuildServiceProvider(); | |||
_builder = new ApplicationBuilder(provider); | |||
using (var scoped = provider.GetRequiredService<IServiceScopeFactory>().CreateScope()) | |||
using (var db = scoped.ServiceProvider.GetRequiredService<CapDbContext>()) | |||
{ | |||
db.Database.EnsureCreated(); | |||
} | |||
} | |||
[Fact] | |||
public async Task EnsureStartupUsageWorks() | |||
{ | |||
var messageStore = _builder.ApplicationServices.GetRequiredService<ICapMessageStore>(); | |||
var messageManager = _builder.ApplicationServices.GetRequiredService<ICapMessageStore>(); | |||
Assert.NotNull(messageStore); | |||
Assert.NotNull(messageManager); | |||
var message = new CapSentMessage(); | |||
var operateResult = await messageManager.StoreSentMessageAsync(message); | |||
Assert.True(operateResult.Succeeded); | |||
operateResult = await messageManager.RemoveSentMessageAsync(message); | |||
Assert.True(operateResult.Succeeded); | |||
} | |||
} | |||
} |
@@ -37,10 +37,12 @@ | |||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" /> | |||
<PackageReference Include="System.Data.SqlClient" Version="4.3.1" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" /> | |||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> | |||
</ItemGroup> | |||
@@ -1,12 +0,0 @@ | |||
using Xunit; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public class EnsuranceTest : DatabaseTestHost | |||
{ | |||
[Fact] | |||
public void Ensure() | |||
{ | |||
} | |||
} | |||
} |
@@ -1,73 +1,58 @@ | |||
using System; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Infrastructure; | |||
using DotNetCore.CAP.Test; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Xunit; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public class MessageStoreTest : MessageManagerTestBase, IClassFixture<ScratchDatabaseFixture> | |||
public class MessageStoreTest : DatabaseTestHost | |||
{ | |||
private readonly ScratchDatabaseFixture _fixture; | |||
public MessageStoreTest(ScratchDatabaseFixture fixture) | |||
{ | |||
_fixture = fixture; | |||
} | |||
public class ApplicationDbContext : CapDbContext | |||
{ | |||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) | |||
{ | |||
} | |||
} | |||
[Fact] | |||
public void CanCreateSentMessageUsingEF() | |||
{ | |||
using (var db = CreateContext()) | |||
{ | |||
var guid = Guid.NewGuid().ToString(); | |||
db.CapSentMessages.Add(new CapSentMessage | |||
var message = new CapSentMessage | |||
{ | |||
Id = guid, | |||
Content = "this is message body", | |||
StateName = StateName.Enqueued | |||
}); | |||
}; | |||
db.Attach(message).State = Microsoft.EntityFrameworkCore.EntityState.Added; | |||
db.SaveChanges(); | |||
Assert.True(db.CapSentMessages.Any(u => u.Id == guid)); | |||
Assert.NotNull(db.CapSentMessages.FirstOrDefault(u => u.StateName == StateName.Enqueued)); | |||
} | |||
} | |||
[Fact] | |||
public async Task CanCreateUsingManager() | |||
{ | |||
var manager = CreateManager(); | |||
var guid = Guid.NewGuid().ToString(); | |||
var message = new CapSentMessage | |||
{ | |||
Id = guid, | |||
Content = "this is message body", | |||
StateName = StateName.Enqueued, | |||
}; | |||
//[Fact] | |||
//public async Task CanCreateUsingManager() | |||
//{ | |||
// var manager = CreateManager(); | |||
// var guid = Guid.NewGuid().ToString(); | |||
// var message = new CapSentMessage | |||
// { | |||
// Id = guid, | |||
// Content = "this is message body", | |||
// StateName = StateName.Enqueued, | |||
// }; | |||
var result = await manager.StoreSentMessageAsync(message); | |||
Assert.NotNull(result); | |||
Assert.True(result.Succeeded); | |||
// var result = await manager.StoreSentMessageAsync(message); | |||
// Assert.NotNull(result); | |||
// Assert.True(result.Succeeded); | |||
result = await manager.RemoveSentMessageAsync(message); | |||
Assert.NotNull(result); | |||
Assert.True(result.Succeeded); | |||
} | |||
// result = await manager.RemoveSentMessageAsync(message); | |||
// Assert.NotNull(result); | |||
// Assert.True(result.Succeeded); | |||
//} | |||
public CapDbContext CreateContext(bool delete = false) | |||
public TestDbContext CreateContext(bool delete = false) | |||
{ | |||
var db = DbUtil.Create<CapDbContext>(_fixture.ConnectionString); | |||
var db = Provider.GetRequiredService<TestDbContext>(); | |||
if (delete) | |||
{ | |||
db.Database.EnsureDeleted(); | |||
@@ -75,31 +60,5 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
db.Database.EnsureCreated(); | |||
return db; | |||
} | |||
protected override object CreateTestContext() | |||
{ | |||
return CreateContext(); | |||
} | |||
protected override void AddMessageStore(IServiceCollection services, object context = null) | |||
{ | |||
services.AddSingleton<ICapMessageStore>(new CapMessageStore<CapDbContext>((CapDbContext)context)); | |||
} | |||
protected override CapSentMessage CreateTestSentMessage(string content = "") | |||
{ | |||
return new CapSentMessage | |||
{ | |||
Content = content | |||
}; | |||
} | |||
protected override CapReceivedMessage CreateTestReceivedMessage(string content = "") | |||
{ | |||
return new CapReceivedMessage() | |||
{ | |||
Content = content | |||
}; | |||
} | |||
} | |||
} | |||
} |
@@ -29,7 +29,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
var connectionString = ConnectionUtil.GetConnectionString(); | |||
//services.AddSingleton(new SqlServerOptions { ConnectionString = connectionString }); | |||
services.AddDbContext<CapDbContext>(options => options.UseSqlServer(connectionString)); | |||
services.AddDbContext<TestDbContext>(options => options.UseSqlServer(connectionString)); | |||
_services = services; | |||
} | |||
@@ -1,22 +0,0 @@ | |||
using System; | |||
using Microsoft.EntityFrameworkCore.Internal; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public class ScratchDatabaseFixture : IDisposable | |||
{ | |||
private LazyRef<SqlServerTestStore> _testStore; | |||
public ScratchDatabaseFixture() { | |||
_testStore = new LazyRef<SqlServerTestStore>(() => SqlServerTestStore.CreateScratch()); | |||
} | |||
public string ConnectionString => _testStore.Value.Connection.ConnectionString; | |||
public void Dispose() { | |||
if (_testStore.HasValue) { | |||
_testStore.Value?.Dispose(); | |||
} | |||
} | |||
} | |||
} |
@@ -1,139 +0,0 @@ | |||
using System; | |||
using System.Data.Common; | |||
using System.Data.SqlClient; | |||
using System.IO; | |||
using System.Threading; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public class SqlServerTestStore : IDisposable | |||
{ | |||
public const int CommandTimeout = 90; | |||
public static string CreateConnectionString(string name) { | |||
var connStrBuilder = new SqlConnectionStringBuilder(TestEnvironment.Config["Test:SqlServer:DefaultConnectionString"]) { | |||
InitialCatalog = name | |||
}; | |||
return connStrBuilder.ConnectionString; | |||
} | |||
public static SqlServerTestStore CreateScratch(bool createDatabase = true) | |||
=> new SqlServerTestStore(GetScratchDbName()).CreateTransient(createDatabase); | |||
private SqlConnection _connection; | |||
private readonly string _name; | |||
private bool _deleteDatabase; | |||
private SqlServerTestStore(string name) { | |||
_name = name; | |||
} | |||
private static string GetScratchDbName() { | |||
string name; | |||
do { | |||
name = "Scratch_" + Guid.NewGuid(); | |||
} while (DatabaseExists(name) | |||
|| DatabaseFilesExist(name)); | |||
return name; | |||
} | |||
private static void WaitForExists(SqlConnection connection) { | |||
var retryCount = 0; | |||
while (true) { | |||
try { | |||
connection.Open(); | |||
connection.Close(); | |||
return; | |||
} | |||
catch (SqlException e) { | |||
if (++retryCount >= 30 | |||
|| (e.Number != 233 && e.Number != -2 && e.Number != 4060)) { | |||
throw; | |||
} | |||
SqlConnection.ClearPool(connection); | |||
Thread.Sleep(100); | |||
} | |||
} | |||
} | |||
private SqlServerTestStore CreateTransient(bool createDatabase) { | |||
_connection = new SqlConnection(CreateConnectionString(_name)); | |||
if (createDatabase) { | |||
using (var master = new SqlConnection(CreateConnectionString("master"))) { | |||
master.Open(); | |||
using (var command = master.CreateCommand()) { | |||
command.CommandTimeout = CommandTimeout; | |||
command.CommandText = $"{Environment.NewLine}CREATE DATABASE [{_name}]"; | |||
command.ExecuteNonQuery(); | |||
WaitForExists(_connection); | |||
} | |||
} | |||
_connection.Open(); | |||
} | |||
_deleteDatabase = true; | |||
return this; | |||
} | |||
private static bool DatabaseExists(string name) { | |||
using (var master = new SqlConnection(CreateConnectionString("master"))) { | |||
master.Open(); | |||
using (var command = master.CreateCommand()) { | |||
command.CommandTimeout = CommandTimeout; | |||
command.CommandText = $@"SELECT COUNT(*) FROM sys.databases WHERE name = N'{name}'"; | |||
return (int)command.ExecuteScalar() > 0; | |||
} | |||
} | |||
} | |||
private static bool DatabaseFilesExist(string name) { | |||
var userFolder = Environment.GetEnvironmentVariable("USERPROFILE") ?? | |||
Environment.GetEnvironmentVariable("HOME"); | |||
return userFolder != null | |||
&& (File.Exists(Path.Combine(userFolder, name + ".mdf")) | |||
|| File.Exists(Path.Combine(userFolder, name + "_log.ldf"))); | |||
} | |||
private void DeleteDatabase(string name) { | |||
using (var master = new SqlConnection(CreateConnectionString("master"))) { | |||
master.Open(); | |||
using (var command = master.CreateCommand()) { | |||
command.CommandTimeout = CommandTimeout; | |||
// Query will take a few seconds if (and only if) there are active connections | |||
// SET SINGLE_USER will close any open connections that would prevent the drop | |||
command.CommandText | |||
= string.Format(@"IF EXISTS (SELECT * FROM sys.databases WHERE name = N'{0}') | |||
BEGIN | |||
ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; | |||
DROP DATABASE [{0}]; | |||
END", name); | |||
command.ExecuteNonQuery(); | |||
} | |||
} | |||
} | |||
public DbConnection Connection => _connection; | |||
public void Dispose() { | |||
_connection.Dispose(); | |||
if (_deleteDatabase) { | |||
DeleteDatabase(_name); | |||
} | |||
} | |||
} | |||
} |
@@ -1,20 +0,0 @@ | |||
using System.IO; | |||
using Microsoft.Extensions.Configuration; | |||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||
{ | |||
public class TestEnvironment | |||
{ | |||
public static IConfiguration Config { get; } | |||
static TestEnvironment() { | |||
var configBuilder = new ConfigurationBuilder() | |||
.SetBasePath(Directory.GetCurrentDirectory()) | |||
.AddJsonFile("config.json", optional: true) | |||
.AddJsonFile("config.test.json", optional: true) | |||
.AddEnvironmentVariables(); | |||
Config = configBuilder.Build(); | |||
} | |||
} | |||
} |
@@ -1,7 +0,0 @@ | |||
{ | |||
"Test": { | |||
"SqlServer": { | |||
"DefaultConnectionString": "Server=192.168.2.206;Initial Catalog=Test;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True" | |||
} | |||
} | |||
} |