I have a solution that is built on Visual Studio Online.
All the unit tests in this solution require a database, I tried to get the build use a test's dedicated localdb (by adding a light mdf file in the test project and use a localdb connection string) but it fail with this error (everything work fine on my desktop) :
System.Data.SqlClient.SqlException: Connection Timeout Expired. The timeout period elapsed while attempting to consume the pre-login handshake acknowledgement. This could be because the pre-login handshake failed or the server was unable to respond back in time. The duration spent while attempting to connect to this server was - [Pre-Login] initialization=29460; handshake=161; ---> System.ComponentModel.Win32Exception: The wait operation timed out.
EDIT
The connection string is
<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" />
I use EF6 code first, repository and unit of work patterns to access it.
Here is the 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());
}
}
The DbContext is instanciated using a unitOfWorkScope :
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);
}
}
which allows me to do things like this in the test (and in the app)
[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);
}
}
Is this scenario even possible? is there any other way to do that?
This very likely has to do with LocalDb not being initialized on the VSO build server that gets spun up to run the build.
According to https://github.com/ritterim/automation-sql, LocalDb may be installed, but not initialized.
LocalDB installed but can't Connect
You may have LocalDB installed, but never initialized the instance on
your machine. Run this command via command prompt.
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 Verify that the command worked by using SQL
Server Management Studio to connect to the instance.
My solution:
Every time VSO launches a build, it creates a brand new virtual machine from a template to run the build. In my case, I wanted to create a small LocalDb database dynamically in order to run unit tests against.
I'm using the RimDev.Automation.Sql nuget package (http://www.nuget.org/packages/RimDev.Automation.Sql/) to allow my tests to programmatically create the database.
I also wanted to integrate with Unity and Entity Framework 6 so that it mimics my database access in production as closely as possible. To do this, I created a based class that all of my tests inherit from. One thing that I wanted is for all of my tests to share the same database, so I made the LocalDb creation static.
In addition, the initialization runs a set of scripts in the Resources folder in order to pre-populate the database with stock data if you so wish. You'll need to make sure that the sql scripts are marked to copy to the output folder (see the properties) so that the files will be in the proper path when the unit test is running.
If you want to create a new database per test class or even per test, this could easily be changed.
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;
}
}
}
}
Here is a sample test:
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);
}
}
}
Lastly, in my test project, I have the test entities object.
using Playground.Model;
namespace Playground.UnitTests
{
public class PlaygroundEntitiesTest : PlaygroundEntities
{
private PlaygroundEntitiesTest()
{
}
public PlaygroundEntitiesTest(string connectionString) : base(connectionString)
{
}
}
}
In my models project, I have my entity and my context.
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; }
}
}
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; }
}
}
Lastly, I set up a post-build step in the unit test project to execute the command to initialize LocalDb as follows:
The full command is
"C:\Program Files\Microsoft SQL Server\120\Tools\Binn\SqlLocalDB.exe"
create "v12.0" 12.0 -s
Then, it was as simple as pushing to Visual Studio Online and launching my build.
Related
I have a console app that I'm using for an azure webjob. I need to have a unique nhibernate session per azure webjob request. I'm using autofact to manage DI.
How can I get Per Request Lifetime instancing in azure webjobs? Inherently a console app doesn't have this. Do I need to change project types?
I've seen several answers on how to do something similar here and here. But they basically boil down to passing in a container as a parameter to functions. That's not really instance per request.
As far as I know, the webjob doesn't have the request. It just run programs as background processes on App Service Web Apps. It couldn't get the request.
In my opinion, the Per Request Lifetime instancing is used in web application like ASP.NET web forms and MVC applications not webjobs.
What do you mean of the request?
Normally, we will use the Instance Per Dependency in the webjobs by using AutofacJobActivator.
It will auto create new instance when the function is triggered.
Here is a webjob example:
class Program
{
// Please set the following connection strings in app.config for this WebJob to run:
// AzureWebJobsDashboard and AzureWebJobsStorage
static void Main()
{
var builder = new ContainerBuilder();
builder.Register(c =>
{
var model = new DeltaResponse();
return model;
})
.As<IDropboxApi>()
.SingleInstance();
builder.RegisterType<Functions>().InstancePerDependency();
var Container = builder.Build();
var config = new JobHostConfiguration()
{
JobActivator = new AutofacJobActivator(Container)
};
var host = new JobHost(config);
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
}
public class AutofacJobActivator : IJobActivator
{
private readonly IContainer _container;
public AutofacJobActivator(IContainer container)
{
_container = container;
}
public T CreateInstance<T>()
{
return _container.Resolve<T>();
}
}
public interface IDropboxApi
{
void GetDelta();
}
public class DeltaResponse : IDropboxApi
{
public Guid id { get; set; }
public DeltaResponse()
{
id = Guid.NewGuid();
}
void IDropboxApi.GetDelta()
{
Console.WriteLine(id);
//throw new NotImplementedException();
}
}
Functions.cs:
public class Functions
{
// This function will get triggered/executed when a new message is written
// on an Azure Queue called queue.
private readonly IDropboxApi _dropboxApi;
public Functions(IDropboxApi dropboxApi)
{
_dropboxApi = dropboxApi;
}
public void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log)
{
log.WriteLine("started");
// Define request parameters.
_dropboxApi.GetDelta();
}
}
When the function triggered, it will auto create new instance.
Disclaimer: I have already read and seen LOTS of tutorials/videos and unfortunately am still not clear on how this works. What I know:
At application Startup ConfigureAuth is executed which contains
app.CreatePerOwinContext(GE.Core.DbContexts.ApplicationDbContext.Create);
This in turn goes to ApplicationDbContext class and establishes a connection
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, Role, int, UserLogin, UserRole, UserClaim>
{
public DbSet<User> geUsers { get; set; }
public ApplicationDbContext(string connString)
: base(connString)
{
Database.SetInitializer(new MySqlInitializer());
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext("name=GEContext");
}
}
Question 1: is there a way to not establish this connection until the login page appears? OR What if i keep a dummy connection in my we.config which should be invalid as soon as i sign-in and replaced with the actual connection?
After the login page is loaded, i take a schoolCode at Login and want to establish a database connection based on that schoolCode. e.g. name = GEContext_[schoolCode]. My Web.config will contain many connectionStrings like this.
Question 2 When I Debug my application, every time a page is loaded, Create method of ApplicationDbContext is hit and a default connection
return new ApplicationDbContext("name=GEContext");
always seems to establish..What's actually going on?
Here is my remaining code:
ApplicationUserManager
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser, Role, int, UserLogin, UserRole, UserClaim>(context.Get<ApplicationDbContext>()));
....
}
}
I have another GEContext
[DbConfigurationType(typeof(MySql.Data.Entity.MySqlEFConfiguration))]
public class GEContext : DbContext
{
public GEContext(string connString):base(connString)
{
this.Configuration.LazyLoadingEnabled = false;
Database.SetInitializer(new MySqlInitializer());
}
}
which works fine from the controller
private static string schoolCode = (string)System.Web.HttpContext.Current.Session["SchoolCode"];
[Authorize]
public ActionResult About()
{
YearRepository repYear = new YearRepository("name=GEContext_" + schoolCode);
}
How do i do the same with applicationdbcontext? but above all questions how does it work?
I am using the following code in a unit test for the test setup:
var simpleEntity = new SimpleEntity();
var complexEntity = new ComplexEntity
{
JoinEntity1List = new List<JoinEntity1>
{
new JoinEntity1
{
JoinEntity2List = new List<JoinEntity2>
{
new JoinEntity2
{
SimpleEntity = simpleEntity
}
}
}
}
};
var anotherEntity = new AnotherEntity
{
ComplexEntity = complexEntity1
};
using (var context = databaseFixture.GetContext())
{
context.Add(anotherEntity);
await context.SaveChangesAsync();
}
When SaveChangesAsync is reached EF throws an ArgumentException with the following message:
An item with the same key has already been added. Key: 1
I'm using a fixture as well for the unit test class which populates the database with objects of the same types, though for this test I want this particular setup so I want to add these new entities to the in memory database. I've tried adding the entities on the DbSet (not the DbContext) and adding all three entities separatly to no avail. I can however add "simpleEntity" separately (because it is not added in the fixture) but EF complains as soon as I try to add "complexEntity" or "anotherEntity".
It seems like EF in memory database cannot handle several Add's over different instances of the context. Is there any workaround for this or am I doing something wrong in my setup?
The databaseFixture in this case is an instance of this class:
namespace Test.Shared.Fixture
{
using Data.Access;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
public class InMemoryDatabaseFixture : IDatabaseFixture
{
private readonly DbContextOptions<MyContext> contextOptions;
public InMemoryDatabaseFixture()
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
var builder = new DbContextOptionsBuilder<MyContext>();
builder.UseInMemoryDatabase()
.UseInternalServiceProvider(serviceProvider);
contextOptions = builder.Options;
}
public MyContext GetContext()
{
return new MyContext(contextOptions);
}
}
}
You can solve this problem by using Collection Fixtures so you can share this fixture across several test classes. This way you don't build you context several times and thus you won't get this exception:
Some information about collection Fixture
My own example:
[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{ }
[Collection("Database collection")]
public class GetCitiesCmdHandlerTests : IClassFixture<MapperFixture>
{
private readonly TecCoreDbContext _context;
private readonly IMapper _mapper;
public GetCitiesCmdHandlerTests(DatabaseFixture dbFixture, MapperFixture mapFixture)
{
_context = dbFixture.Context;
_mapper = mapFixture.Mapper;
}
[Theory]
[MemberData(nameof(HandleTestData))]
public async void Handle_ShouldReturnCountries_AccordingToRequest(
GetCitiesCommand command,
int expectedCount)
{
(...)
}
public static readonly IEnumerable<object[]> HandleTestData
= new List<object[]>
{
(...)
};
}
}
Good luck,
Seb
I'm using EF6 code-first and at the first, I put the connection string in a text file called 'Settings.txt'
The data in the 'Settings.txt' file is
DataProvider: sqlserver
DataConnectionString: Data Source=.\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;
Here what I use for the dbContext class:
public class DbDataContext : BaseDbContext
{
static DbDataContext()
{
Database.SetInitializer(new ContextInitializer());
}
public DbDataContext():base() { }
public DbDataContext(string nameOrConnectionString)
: base(nameOrConnectionString) { }
...
}
[DbConfigurationType(typeof(MyDbConfiguration))]
public abstract partial class BaseDbContext : DbContext, IDbContext
{
public BaseDbContext() : this(GetConnectionString())
{ }
public BaseDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
{ }
public static string GetConnectionString()
{
if (DataSettings.DataSettings.Current.IsValid())
{
return DataSettings.DataSettings.Current.DataConnectionString;
}
throw Error.Application("A connection string could not be resolved for the parameterless constructor of the derived DbContext. Either the database is not installed, or the file 'Settings.txt' does not exist or contains invalid content.");
}
}
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
IEfDataProvider provider = null;
try
{
provider = (new EfDataProviderFactory(DataSettings.DataSettings.Current).LoadDataProvider()) as IEfDataProvider;
}
catch {
}
if (provider != null)
{
base.SetDefaultConnectionFactory(provider.GetConnectionFactory());
}
}
}
public partial class EfDataProviderFactory : DataProviderFactory
{
public EfDataProviderFactory()
: this(DataSettings.DataSettings.Current){ }
public EfDataProviderFactory(DataSettings.DataSettings settings)
: base(settings) { }
public override IDataProvider LoadDataProvider()
{
var providerName = Settings.DataProvider;
if (providerName.IsEmpty())
{
throw new Exception("Data Settings doesn't contain a providerName");
}
switch (providerName.ToLowerInvariant())
{
case "sqlserver":
return new SqlServerDataProvider();
case "sqlserverce":
return new SqlServerCeDataProvider();
default:
throw new Exception(string.Format("Unsupported dataprovider name: {0}", providerName));
}
}
}
public class SqlServerDataProvider : IEfDataProvider
{
public virtual IDbConnectionFactory GetConnectionFactory()
{
return new SqlConnectionFactory();
}
public bool StoredProceduresSupported
{
get { return false; }
}
public DbParameter GetParameter()
{
return new SqlParameter();
}
public string ProviderInvariantName
{
get { return "System.Data.SqlClient"; }
}
}
I use a static function in 'BaseDbContext' class called 'GetConnectionString()'
This function just for return the connection string from the text file. This behavior is working very well at runtime, but it not working when adding a migration.
This is the problem: how can I add a migration by this way, knowing that when I put the connection string directly in the function like this
public static string GetConnectionString()
{
return (#"Data Source=.\\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;");
}
the Add-Migration command is working
How can I solve this problem without forcing the connection string in the code?
I solved this,
the problem occurs in getting the file path (text file) in design-mode or even
unit tests
string filePath = Path.Combine(MapPath("~/App_Data/"), "Settings.txt");
public static string MapPath(string path)
{
path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
var testPath = Path.Combine(baseDirectory, path);
var dir = FindSolutionRoot(baseDirectory);
if (dir != null)
{
baseDirectory = Path.Combine(dir.FullName, "MyProjectName.WebAPI");
testPath = Path.Combine(baseDirectory, path);
return testPath;
}
}
private static DirectoryInfo FindSolutionRoot(string currentDir)
{
var dir = Directory.GetParent(currentDir);
while (true)
{
if (dir == null || IsSolutionRoot(dir))
break;
dir = dir.Parent;
}
return dir;
}
private static bool IsSolutionRoot(DirectoryInfo dir)
{
return File.Exists(Path.Combine(dir.FullName, "MySolutionName.sln"));
}
and by this, we can get the file path in runtime-mode
I guess you use the Add-Migration command in the Package Manager Console.
If you are running Add-Migration manually you can just add the connection string as the -ConnectionString parameter:
Add-Migration -ConnectionString "Data Source=.\\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;"
You will probably have to add parameter -ConnectionProviderName as well, unless you have a provider defined in your app.config.
I would recommend you to stop using this Settings.txt file and move your connection string to your app.config file, in the section connectionStrings. This is the recommended way to deal with connection strings, much easier than using a custom file like your Settings.txt file.
<connectionStrings>
<add name="MyLocalDatabase" connectionString="Data Source=.\\SQLEXPRESS;Initial Catalog=MyDb;Integrated Security=True;Persist Security Info=False;Enlist=False;" />
</connectionStrings>
If you do that you can use the parameter -ConnectionStringName in the Package Manager Console, using the name you defined in the app.config:
Add-Migration -ConnectionStringName "MyLocalDatabase"
Also, with the connection string in your app.config file you can add a constructor to your context that receives the connection string name as a parameter and can be used when using the Package Manager console:
public DbDataContext():base("MyLocalDatabase") { }
This will allow you to run your commands in Package Manager Console without specifying any connection string parameters at all. Just make sure the right connection strings are included in the app.config file of the start project selected in the console.
And you can get rid of your GetConnectionString code. You are just re-implementing code that you have out-of-the-box when using app.settings connectionString section. That's how DbContext base constructors parameter NameOrConnectionString is meant to be used. You can provide either a full connection string or the name of a connection string defined in the app.settings file.
namespace MoqSample.Test
{
[TestFixture]
public class GivenCustomerServiceTest
{
private ICustomerService customerService;
private CustomerModel customer;
// Defining the mock object.
private Mock<ICustomerRepository> mockCustomerRepository;
[SetUp]
public void SetUp()
{
//Creating the mock object.
mockCustomerRepository = new Mock<ICustomerRepository>();
customerService = new CustomerService(mockCustomerRepository.Object);
}
[Test]
public void GetCustomerByIdTest()
{
customer = new CustomerModel { Id = 1, Name = "TEST-CUSTOMER", Address = "abc" };
mockCustomerRepository.Setup(customerRepository => customerRepository.GetCustomerById(1)).Returns(customer);
var customerReturned = customerService.GetCustomerById(1);
//Verifying values.
Assert.AreEqual(customer.Id, customerReturned.Id);
Assert.AreEqual(customer.Name, customerReturned.Name);
Assert.AreEqual(customer.Address, customerReturned.Address);
}
}
}
When I am trying to debug the code in aforementioned class , it's not hitting the break point.
I.e I am unable to debug the code.
Any suggestions are welcome.
Using the Resharper or TestDriven test runners should allow you to debug through your unit tests.