Axe*_*xel 9 localdb azure-devops
我有一个基于Visual Studio Online的解决方案.此解决方案中的所有单元测试都需要一个数据库,我试图让构建使用测试的专用localdb(通过在测试项目中添加一个light mdf文件并使用localdb连接字符串)但是它失败并出现此错误(evrything工作正常)在我的桌面上):
System.Data.SqlClient.SqlException:连接超时已过期.尝试使用登录前握手确认时超时时间已过.这可能是因为登录前握手失败或服务器无法及时响应.尝试连接到此服务器所花费的时间是 - [Pre-Login] initialization = 29460; 握手= 161; ---> System.ComponentModel.Win32Exception:等待操作超时.
编辑
连接字符串是
<add name="TestDb" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=AeroBase;Integrated Security=true;AttachDBFilename=|DataDirectory|\App_Data\AeroBase.mdf" providerName="System.Data.SqlClient" />
Run Code Online (Sandbox Code Playgroud)
我首先使用EF6代码,存储库和工作模式单元来访问它.这是DbContext:
public class AeroDataContext : DbContext, IDbContext
{
private Guid DataContextId;
private string _name;
public string Name { get { return _name; } }
public AeroDataContext(string cnxStringName, string cnxString)
: base(cnxString)
{
this.Database.Log = delegate(String name)
{
// Debug.WriteLine(name);
};
_name = cnxStringName;
this.Configuration.LazyLoadingEnabled = false;
DataContextId = Guid.NewGuid();
Debug.WriteLine("AeroDbCreation Id = " + DataContextId.ToString());
}
}
Run Code Online (Sandbox Code Playgroud)
使用unitOfWorkScope实例化DbContext:
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private string _cnxStringName;
private string _cnxString;
public UnitOfWorkFactory(string cnxStringName, string cnxString)
{
_cnxString = cnxString;
_cnxStringName = cnxStringName;
}
public IUnitOfWorkScope GetUnitOfWorkScope(bool disposeAtEndOfContext = true)
{
return new UnitOfWorkScope(new AeroDataContext(_cnxStringName, _cnxString), disposeAtEndOfContext);
}
}
Run Code Online (Sandbox Code Playgroud)
允许我在测试中(以及在应用程序中)做这样的事情
[TestMethod]
public void DB_Get_LFFE_Airport_By_ICAOInclude_SubType()
{
//structuremap container built in the TestInitilized method
IUnitOfWorkFactory _uowf = container.GetInstance<IUnitOfWorkFactory>();
using (IUnitOfWorkScope uows = _uowf.GetUnitOfWorkScope(true))
{
IAirportRepository repo = uows.GetRepository<IAirportRepository>();
Airport ar = repo.SearchByICAO("LFFE").FirstOrDefault();
AirportValidator.LFFE(ar);
}
}
Run Code Online (Sandbox Code Playgroud)
这个场景甚至可能吗?还有其他办法吗?
谢谢
Dav*_*son 13
这很可能与在VSO构建服务器上初始化LocalDb有关,而VSO构建服务器是为了运行构建而运行的.
根据https://github.com/ritterim/automation-sql,可以安装LocalDb,但不能初始化.
已安装LocalDB但无法连接
您可能已安装LocalDB,但从未在计算机上初始化该实例.通过命令提示符运行此命令.
LocalDB SQL EXPRESS 2014
"C:\ Program Files\Microsoft SQL Server\120\Tools\Binn\SqlLocalDB.exe"create"v12.0"12.0 -s LocalDB SQL Express 2012
"C:\ Program Files\Microsoft SQL Server\110\Tools\Binn\SqlLocalDB.exe"create"v11.0"11.0 -s通过使用SQL Server Management Studio连接到实例来验证该命令是否有效.
我的解决方案
每次VSO启动构建时,它都会从模板创建一个全新的虚拟机来运行构建.在我的例子中,我想动态创建一个小的LocalDb数据库,以便运行单元测试.
我正在使用RimDev.Automation.Sql nuget包(http://www.nuget.org/packages/RimDev.Automation.Sql/)来允许我的测试以编程方式创建数据库.
我还希望与Unity和Entity Framework 6集成,以便尽可能地模拟我在生产中的数据库访问.为此,我创建了一个基于我所有测试继承的基类.我想要的一件事是我的所有测试共享同一个数据库,所以我将LocalDb创建为静态.
此外,初始化在Resources文件夹中运行一组脚本,以便根据需要使用库存数据预填充数据库.您需要确保将sql脚本标记为复制到输出文件夹(请参阅属性),以便在运行单元测试时文件将位于正确的路径中.
如果要为每个测试类甚至每个测试创建一个新数据库,可以轻松更改.
using System;
using System.Data.Entity;
using System.IO;
using Microsoft.Practices.Unity;
using Playground.Model;
using RimDev.Automation.Sql;
namespace Playground.UnitTests
{
public abstract class TestBaseClass
{
// For now, these are static. We may want to change them at some point
// to be per class so we create separate databases for each class run, but
// for now, let's not do that for speed sake.
private static readonly IUnityContainer _container = new UnityContainer();
private static bool _isRegistered = false;
private static readonly object _syncRoot = new object();
private static LocalDb LocalDb = new LocalDb(databaseName: "PlaygroundTestDb", databasePrefix: "pg", version: "v12.0");
private static bool _isInitialized = false;
protected TestBaseClass()
{
RegisterComponents(_container);
InitializeData();
_container.BuildUp(GetType(), this);
}
private void InitializeData()
{
lock (_syncRoot)
{
if (!_isInitialized)
{
var dbContext = _container.Resolve<PlaygroundEntities>();
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<PlaygroundEntities, Playground.Model.Migrations.Configuration>());
// Make sure database exists.
dbContext.Database.Initialize(true);
foreach (
var f in Directory.GetFiles(Path.Combine(Environment.CurrentDirectory, "Resources"), "*.sql"))
{
dbContext.Database.ExecuteSqlCommand(File.ReadAllText(f));
}
}
_isInitialized = true;
}
}
private void RegisterComponents(IUnityContainer container)
{
lock (_syncRoot)
{
if (!_isRegistered)
{
// WARNING! Most methods in the unity container are not thread safe. See http://unity.codeplex.com/discussions/27496
// We may need to expose protected methods to register certain types. For now, assume all of the
// tests use the same injected objects. If a test REALLY needs to a different dependency, the test can
// manually create it as well.
container.RegisterType<PlaygroundEntities, PlaygroundEntitiesTest>(new TransientLifetimeManager(),
new InjectionConstructor(new object[] {LocalDb.ConnectionString}));
}
_isRegistered = true;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个示例测试:
using System.Linq;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Playground.Model;
namespace Playground.UnitTests
{
[TestClass]
public class UnitTest1 : TestBaseClass
{
[Dependency]
public PlaygroundEntities Db { get; set; }
private static bool _initialized = false;
[TestInitialize]
public void TestInitialize()
{
if (!_initialized)
{
Db.Playgrounds.Add(new Playground.Model.Playground() {Name = "Dave's playground", Location = "SomeTown"});
Db.SaveChanges();
_initialized = true;
}
}
[TestMethod]
public void TestMethod1()
{
var p = Db.Playgrounds.FirstOrDefault(pg => pg.Name == "Dave's playground");
Assert.IsNotNull(p);
}
[TestMethod]
public void TestMethod2()
{
var p = Db.Playgrounds.FirstOrDefault(pg => pg.Location == "SomeTown");
Assert.IsNotNull(p);
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后,在我的测试项目中,我有测试实体对象.
using Playground.Model;
namespace Playground.UnitTests
{
public class PlaygroundEntitiesTest : PlaygroundEntities
{
private PlaygroundEntitiesTest()
{
}
public PlaygroundEntitiesTest(string connectionString) : base(connectionString)
{
}
}
}
Run Code Online (Sandbox Code Playgroud)
在我的模型项目中,我有我的实体和我的上下文.
Playground.cs
using System;
namespace Playground.Model
{
public class Playground
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
PlaygroundEntities.cs
using System.Data.Entity;
namespace Playground.Model
{
public class PlaygroundEntities : DbContext
{
public PlaygroundEntities() : base("PlaygroundConnectionString")
{
}
public PlaygroundEntities(string connectionString) : base(connectionString)
{
}
public virtual DbSet<Playground> Playgrounds { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我在单元测试项目中设置了一个构建后步骤来执行命令以初始化LocalDb,如下所示:
完整的命令是
"C:\ Program Files\Microsoft SQL Server\120\Tools\Binn\SqlLocalDB.exe"create"v12.0"12.0 -s
然后,就像推送到Visual Studio Online并启动我的构建一样简单.

| 归档时间: |
|
| 查看次数: |
3273 次 |
| 最近记录: |