// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. namespace System.Data.Entity.Config { using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Configuration; using System.Data.Common; using System.Data.Entity.Core.Common; using System.Data.Entity.Infrastructure; using System.Data.Entity.Internal; using System.Data.Entity.Internal.ConfigFile; using System.Data.Entity.Migrations.Sql; using System.Data.Entity.ModelConfiguration.Internal.UnitTests; using System.Data.Entity.Spatial; using System.Data.Entity.SqlServer; using System.Data.Entity.Utilities; using System.Linq; using Moq; using Xunit; public class AppConfigDependencyResolverTests : AppConfigTestBase { public interface IPilkington { } public class FakeConnectionFactory : IDbConnectionFactory { public DbConnection CreateConnection(string nameOrConnectionString) { throw new NotImplementedException(); } } [Fact] public void GetService_returns_null_for_unknown_contract_type() { Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService("Karl")); } [Fact] public void GetService_returns_registered_provider() { Assert.Same( ProviderServicesFactoryTests.FakeProviderWithPublicProperty.Instance, new AppConfigDependencyResolver(CreateAppConfigWithProvider()).GetService("Is.Ee.Avin.A.Larf")); } [Fact] public void GetService_returns_null_for_unregistered_provider_name() { Assert.Null( new AppConfigDependencyResolver(CreateAppConfigWithProvider()).GetService("Are.You.Avin.A.Larf")); } [Fact] public void GetService_returns_null_for_null_empty_or_whitespace_provider_name() { Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService(null)); Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService("")); Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService(" ")); } [Fact] public void GetService_caches_provider() { var mockProviders = new Mock(); mockProviders.Setup(m => m.TryGetDbProviderServices("Ask.Rhod.Gilbert")).Returns(new Mock().Object); var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Providers).Returns(mockProviders.Object); var resolver = new AppConfigDependencyResolver(mockConfig.Object); var factoryInstance = resolver.GetService("Ask.Rhod.Gilbert"); Assert.NotNull(factoryInstance); mockProviders.Verify(m => m.TryGetDbProviderServices("Ask.Rhod.Gilbert"), Times.Once()); Assert.Same(factoryInstance, resolver.GetService("Ask.Rhod.Gilbert")); mockProviders.Verify(m => m.TryGetDbProviderServices("Ask.Rhod.Gilbert"), Times.Once()); } [Fact] public void GetService_caches_the_fact_that_no_provider_is_registered() { var mockProviders = new Mock(); mockProviders.Setup(m => m.TryGetDbProviderServices("Ask.Rhod.Gilbert")).Returns((DbProviderServices)null); var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Providers).Returns(mockProviders.Object); var resolver = new AppConfigDependencyResolver(mockConfig.Object); Assert.Null(resolver.GetService("Ask.Rhod.Gilbert")); mockProviders.Verify(m => m.TryGetDbProviderServices("Ask.Rhod.Gilbert"), Times.Once()); Assert.Null(resolver.GetService("Ask.Rhod.Gilbert")); mockProviders.Verify(m => m.TryGetDbProviderServices("Ask.Rhod.Gilbert"), Times.Once()); } [Fact] public void GetService_returns_registered_Migrations_SQL_generator() { Assert.IsType( new AppConfigDependencyResolver( CreateAppConfigWithProvider( typeof(ProviderConfigTests.TryGetMigrationSqlGeneratorFactory.MySqlGenerator).AssemblyQualifiedName)) .GetService("Is.Ee.Avin.A.Larf")); } [Fact] public void GetService_returns_null_SQL_generator_for_unregistered_provider() { Assert.Null( new AppConfigDependencyResolver(CreateAppConfigWithProvider()).GetService("Are.You.Avin.A.Larf")); } [Fact] public void GetService_returns_null_for_registered_provider_with_no_registered_Migrations_SQL_generator() { Assert.Null( new AppConfigDependencyResolver(CreateAppConfigWithProvider()).GetService("Is.Ee.Avin.A.Larf")); } [Fact] public void GetService_returns_null_when_asked_for_SQL_generator_for_null_empty_or_whitespace_provider_name() { Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService(null)); Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService("")); Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService(" ")); } [Fact] public void GetService_caches_Migrations_SQL_generator_and_uses_it_to_return_a_new_instance_every_call() { var mockProviders = new Mock(); mockProviders.Setup(m => m.TryGetMigrationSqlGeneratorFactory("Ask.Rhod.Gilbert")) .Returns(() => new Mock().Object); var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Providers).Returns(mockProviders.Object); var resolver = new AppConfigDependencyResolver(mockConfig.Object); var migrationSqlGenerator1 = resolver.GetService("Ask.Rhod.Gilbert"); Assert.NotNull(migrationSqlGenerator1); mockProviders.Verify(m => m.TryGetMigrationSqlGeneratorFactory("Ask.Rhod.Gilbert"), Times.Once()); var migrationSqlGenerator2 = resolver.GetService("Ask.Rhod.Gilbert"); Assert.NotNull(migrationSqlGenerator2); Assert.NotSame(migrationSqlGenerator1, migrationSqlGenerator2); mockProviders.Verify(m => m.TryGetMigrationSqlGeneratorFactory("Ask.Rhod.Gilbert"), Times.Once()); } [Fact] public void GetService_caches_the_fact_that_no_Migrations_SQL_generator_is_registered() { var mockProviders = new Mock(); mockProviders.Setup(m => m.TryGetMigrationSqlGeneratorFactory("Ask.Rhod.Gilbert")) .Returns((Func)(() => null)); var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Providers).Returns(mockProviders.Object); var resolver = new AppConfigDependencyResolver(mockConfig.Object); Assert.Null(resolver.GetService("Ask.Rhod.Gilbert")); mockProviders.Verify(m => m.TryGetMigrationSqlGeneratorFactory("Ask.Rhod.Gilbert"), Times.Once()); Assert.Null(resolver.GetService("Ask.Rhod.Gilbert")); mockProviders.Verify(m => m.TryGetMigrationSqlGeneratorFactory("Ask.Rhod.Gilbert"), Times.Once()); } [Fact] public void GetService_returns_connection_factory_set_in_config() { try { Assert.IsType( new AppConfigDependencyResolver( new AppConfig( CreateEmptyConfig().AddDefaultConnectionFactory(typeof(FakeConnectionFactory).AssemblyQualifiedName))) .GetService()); } finally { Database.ResetDefaultConnectionFactory(); } } [Fact] public void GetService_returns_null_if_no_connection_factory_is_set_in_config() { Assert.Null(new AppConfigDependencyResolver(CreateAppConfig()).GetService()); } [Fact] public void GetService_caches_connection_factory() { try { var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.TryGetDefaultConnectionFactory()).Returns(new FakeConnectionFactory()); var resolver = new AppConfigDependencyResolver(mockConfig.Object); var factoryInstance = resolver.GetService(); Assert.NotNull(factoryInstance); mockConfig.Verify(m => m.TryGetDefaultConnectionFactory(), Times.Once()); Assert.Same(factoryInstance, resolver.GetService()); mockConfig.Verify(m => m.TryGetDefaultConnectionFactory(), Times.Once()); } finally { Database.ResetDefaultConnectionFactory(); } } [Fact] public void GetService_caches_the_fact_that_no_connection_factory_is_set() { var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.TryGetDefaultConnectionFactory()).Returns((IDbConnectionFactory)null); var resolver = new AppConfigDependencyResolver(mockConfig.Object); Assert.Null(resolver.GetService()); mockConfig.Verify(m => m.TryGetDefaultConnectionFactory(), Times.Once()); Assert.Null(resolver.GetService()); mockConfig.Verify(m => m.TryGetDefaultConnectionFactory(), Times.Once()); } [Fact] public void GetService_returns_registered_database_initializer() { var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Initializers).Returns( new InitializerConfig( CreateEfSection(initializerDisabled: false), new KeyValueConfigurationCollection())); Assert.IsType>( new AppConfigDependencyResolver(mockConfig.Object).GetService>()); } [Fact] public void GetService_returns_null_for_unregistered_database_initializer() { var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Initializers).Returns( new InitializerConfig( CreateEfSection(initializerDisabled: false), new KeyValueConfigurationCollection())); Assert.Null(new AppConfigDependencyResolver(mockConfig.Object).GetService>()); } [Fact] public void GetService_caches_database_initializer() { var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Initializers).Returns( new InitializerConfig( CreateEfSection(initializerDisabled: false), new KeyValueConfigurationCollection())); var resolver = new AppConfigDependencyResolver(mockConfig.Object); var initializer = resolver.GetService>(); Assert.NotNull(initializer); mockConfig.Verify(m => m.Initializers, Times.Once()); Assert.Same(initializer, resolver.GetService>()); mockConfig.Verify(m => m.Initializers, Times.Once()); } [Fact] public void GetService_caches_the_fact_that_no_database_initializer_is_registered() { var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Initializers).Returns( new InitializerConfig( CreateEfSection(initializerDisabled: false), new KeyValueConfigurationCollection())); var resolver = new AppConfigDependencyResolver(mockConfig.Object); Assert.Null(resolver.GetService>()); mockConfig.Verify(m => m.Initializers, Times.Once()); Assert.Null(resolver.GetService>()); mockConfig.Verify(m => m.Initializers, Times.Once()); } [Fact] public void GetService_returns_registered_spatial_provider() { Assert.IsType( new AppConfigDependencyResolver(CreateAppConfigWithSpatial(typeof(SqlSpatialServices).AssemblyQualifiedName)).GetService ()); } [Fact] public void GetService_returns_null_when_no_provider_registered() { Assert.Null( new AppConfigDependencyResolver(CreateAppConfigWithSpatial()).GetService()); } [Fact] public void GetService_caches_spatial_provider() { var mockProviders = new Mock(); mockProviders.Setup(m => m.TryGetSpatialProvider()).Returns(new Mock().Object); var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Providers).Returns(mockProviders.Object); var resolver = new AppConfigDependencyResolver(mockConfig.Object); var factoryInstance = resolver.GetService(); Assert.NotNull(factoryInstance); mockProviders.Verify(m => m.TryGetSpatialProvider(), Times.Once()); Assert.Same(factoryInstance, resolver.GetService()); mockProviders.Verify(m => m.TryGetSpatialProvider(), Times.Once()); } [Fact] public void GetService_caches_the_fact_that_no_spatial_provider_is_registered() { var mockProviders = new Mock(); mockProviders.Setup(m => m.TryGetSpatialProvider()).Returns((DbSpatialServices)null); var mockConfig = new Mock(new ConnectionStringSettingsCollection()); mockConfig.Setup(m => m.Providers).Returns(mockProviders.Object); var resolver = new AppConfigDependencyResolver(mockConfig.Object); Assert.Null(resolver.GetService()); mockProviders.Verify(m => m.TryGetSpatialProvider(), Times.Once()); Assert.Null(resolver.GetService()); mockProviders.Verify(m => m.TryGetSpatialProvider(), Times.Once()); } private static EntityFrameworkSection CreateEfSection(bool initializerDisabled) { var mockDatabaseInitializerElement = new Mock(); mockDatabaseInitializerElement .Setup(m => m.InitializerTypeName) .Returns(typeof(FakeInitializer).AssemblyQualifiedName); mockDatabaseInitializerElement.Setup(m => m.Parameters).Returns(new ParameterCollection()); var mockContextElement = new Mock(); mockContextElement.Setup(m => m.IsDatabaseInitializationDisabled).Returns(initializerDisabled); mockContextElement.Setup(m => m.ContextTypeName).Returns(typeof(FakeContext).AssemblyQualifiedName); mockContextElement.Setup(m => m.DatabaseInitializer).Returns(mockDatabaseInitializerElement.Object); var mockContextCollection = new Mock(); mockContextCollection.As().Setup(m => m.GetEnumerator()).Returns( new List { mockContextElement.Object }.GetEnumerator()); var mockEfSection = new Mock(); mockEfSection.Setup(m => m.Contexts).Returns(mockContextCollection.Object); return mockEfSection.Object; } public class FakeContext : DbContext { } public class FakeInitializer : IDatabaseInitializer where TContext : DbContext { public void InitializeDatabase(TContext context) { } } [Fact] public void EF_provider_can_be_loaded_from_real_app_config() { Assert.Same( FakeSqlProviderServices.Instance, new AppConfigDependencyResolver(AppConfig.DefaultInstance).GetService("System.Data.FakeSqlClient")); } [Fact] public void SQL_generator_can_be_loaded_from_real_app_config() { Assert.IsType( new AppConfigDependencyResolver(AppConfig.DefaultInstance).GetService("System.Data.FakeSqlClient")); } [Fact] public void Spatial_provider_can_be_loaded_from_real_app_config() { Assert.IsType( new AppConfigDependencyResolver(AppConfig.DefaultInstance).GetService()); } private static AppConfig CreateAppConfigWithProvider(string sqlGeneratorName = null) { return CreateAppConfig( "Is.Ee.Avin.A.Larf", typeof(ProviderServicesFactoryTests.FakeProviderWithPublicProperty).AssemblyQualifiedName, sqlGeneratorName); } /// /// This test makes calls from multiple threads such that we have at least some chance of finding threading /// issues. As with any test of this type just because the test passes does not mean that the code is /// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to /// be investigated. DON'T just re-run and think things are okay if the test then passes. /// [Fact] public void GetService_can_be_accessed_from_multiple_threads_concurrently() { try { var appConfig = new AppConfig( CreateEmptyConfig().AddDefaultConnectionFactory(typeof(FakeConnectionFactory).AssemblyQualifiedName)); for (var i = 0; i < 30; i++) { var bag = new ConcurrentBag(); var resolver = new AppConfigDependencyResolver(appConfig); ExecuteInParallel(() => bag.Add(resolver.GetService())); Assert.Equal(20, bag.Count); Assert.True(bag.All(c => resolver.GetService() == c)); } } finally { Database.ResetDefaultConnectionFactory(); } } } }