We have a custom initializer that does not change the database schema in any way; all it does is seed some data.
I've been asked to come up with a custom initializer that will always drop the database and then populate it with the same seed data (this will be used only in our database test projects).
I have written this initializer:
public class DropAndSeedDbInitializer<TContext, TSeedDataInitializer> : IDatabaseInitializer<TContext>
where TContext : DbContext
where TSeedDataInitializer : IDatabaseInitializer<TContext>, new()
{
private readonly DropCreateDatabaseAlways<TContext> _dbInitializer;
private readonly IDatabaseInitializer<TContext> _seedDataInitializer;
public DropAndSeedDbInitializer()
{
_dbInitializer = new DropCreateDatabaseAlways<TContext>();
_seedDataInitializer = Activator.CreateInstance<TSeedDataInitializer>();
}
public void InitializeDatabase(TContext context)
{
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction
, string.Format("ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE", context.Database.Connection.Database));
_dbInitializer.InitializeDatabase(context);
_seedDataInitializer.InitializeDatabase(context);
}
}
It works fine when I call
LicenceContext context = new LicenceContext();
Database.SetInitializer(new DropAndSeedDbInitializer<LicenceContext, TestSeedDataInitializer>());
context.Database.Initialize(true);
Then I decided to configure it in app.config like so:
<context type="Foo.LicenceContext, MyAssembly">
<databaseInitializer
type="Foo.DropAndSeedDbInitializer`2[[Foo.LicenceContext, MyAssembly], Foo.TestSeedDataInitializer, MyAssembly]], MyAssembly" />
</context>
and I removed this code:
Database.SetInitializer(new DropAndSeedDbInitializer<LicenceContext, TestSeedDataInitializer>());
context.Database.Initialize(true);
But then I get this error the first time I try to access my context:
Failed to set database initializer of type 'Foo.DropAndSeedDbInitializer`2[[Foo.LicenceContext, MyAssembly], Foo.TestSeedDataInitializer, MyAssembly]], MyAssembly' for DbContext type 'Foo.LicenceContext, MyAssembly' specified in the application configuration. See inner exception for details.
The inner exception is:
Exception of type 'System.ArgumentException' was thrown.
Parameter name: typeName#149
at System.Data.Entity.Internal.InitializerConfig.TryGetInitializer(Type requiredContextType, String contextTypeName, String initializerTypeName, Boolean isDisabled, Func`1 initializerArgs, Func`3 exceptionMessage)
at System.Data.Entity.Internal.InitializerConfig.<>c__DisplayClass6.<TryGetInitializerFromEntityFrameworkSection>b__1(ContextElement e)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at System.Data.Entity.Internal.InitializerConfig.TryGetInitializerFromEntityFrameworkSection(Type contextType)
at System.Data.Entity.Internal.InitializerConfig.TryGetInitializer(Type contextType)
at System.Data.Entity.Infrastructure.DependencyResolution.AppConfigDependencyResolver.GetServiceFactory(Type type, String name)
at System.Data.Entity.Infrastructure.DependencyResolution.AppConfigDependencyResolver.<>c__DisplayClass1.<GetService>b__0(Tuple`2 t)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at System.Data.Entity.Infrastructure.DependencyResolution.AppConfigDependencyResolver.GetService(Type type, Object key)
at System.Data.Entity.Infrastructure.DependencyResolution.ResolverChain.<>c__DisplayClass3.<GetService>b__0(IDbDependencyResolver r)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
Then I created a class which inherits from DropAndSeedDbInitializer like so:
public class DummyInitializer : DropAndSeedDbInitializer<LicenceContext, TestSeedDataInitializer>
{
}
And I configured it in app.config like so:
<context type="Foo.LicenceContext, MyAssembly">
<databaseInitializer
type="Foo.DummyInitializer, MyAssembly" />
</context>
And then the DropAndSeedDbInitalizer ran and I could access my context without any problems.
So my question is: should I be able to configure this custom initializer in app.config? If I should, what am I doing wrong?
Thanks,
David
Related
I have the following class (simplified)
DomainComponent]
[VisibleInReports(true)]
public class SpTestResult
{
public string Name {get;set;}
}
And in OnModelCreating I have
modelBuilder.Entity<SpTestResult>().HasNoKey().ToTable(null); ;
The following code
public static EFCoreObjectSpaceProvider GetEfCoreObjectSpaceProvider()
{
var connectionString = GetConnectionString();
var osp = new EFCoreObjectSpaceProvider(typeof(MyDbContext),
(builder, _) => builder.UseSqlServer(connectionString));
return osp;
}
Throws an error
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=DevExpress.ExpressApp.EFCore.v21.2
StackTrace:
at DevExpress.ExpressApp.EFCore.EFCoreTypeInfoSource.SetupKeyMembers(TypeInfo typeInfo)
at DevExpress.ExpressApp.EFCore.EFCoreTypeInfoSource.RegisterEntity(Type type)
at DevExpress.ExpressApp.EFCore.TypeInfoSourceHelper.InitTypeInfoSource(EFCoreObjectSpaceProvider objectSpaceProvider)
at DevExpress.ExpressApp.EFCore.EFCoreObjectSpaceProvider..ctor(Type contextType, ServiceCollection services, DbContextOptions dbContextOptions, ITypesInfo typesInfo, EFCoreTypeInfoSource typeInfoSource, String connectionString, EFCoreDatabaseProviderHandler databaseProviderHandler)
at DevExpress.ExpressApp.EFCore.EFCoreObjectSpaceProvider..ctor(Type contextType, ITypesInfo typesInfo, EFCoreTypeInfoSource typeInfoSource, String connectionString, EFCoreDatabaseProviderHandler databaseProviderHandler)
at DevExpress.ExpressApp.EFCore.EFCoreObjectSpaceProvider..ctor(Type contextType, EFCoreDatabaseProviderHandler databaseProviderHandler)
at MyApp.Module.Functions.DataHelpers.GetEfCoreObjectSpaceProvider() in
The simple work around is to include a primary key in the business object and remove the HasNoKey property in OnModelCreating.
I am wondering if it is necessary to have a primary key.
Yes, if you are going to Xaf approaches like ObjectSpace or show objects in grid etc. Xaf needs a unique Key property on all classes.
The Problem:
I am having trouble getting StructureMap to find the default instance for the IRepository<> type in the MongoRepostiory namespace.
The exception message:
"StructureMap Exception Code: 202
No Default Instance defined for PluginFamily MongoDB.Driver.MongoUrl, MongoDB.Driver, Version=1.8.3.9, Culture=neutral, PublicKeyToken=f686731cfb9cc103"
It appears that StructureMap is instantiating the MonogoRepository class with the wrong constructor...
The MonogoRepository has the following constructors:
public class MongoRepository<T> : MongoRepository<T, string>, IRepository<T>, IRepository<T, string>, IQueryable<T>, IEnumerable<T>, IQueryable, IEnumerable where T : MongoRepository.IEntity<string>
{
public MongoRepository();
public MongoRepository(MongoUrl url);
public MongoRepository(string connectionString);
public MongoRepository(MongoUrl url, string collectionName);
public MongoRepository(string connectionString, string collectionName);
}
I want the public MongoRepository(string connectionString); constructor to be called...but it appears that from the exception message, since StructureMap is attempting to resolve MongoUrl, it is not calling the desired one.
I wonder:
Questions:
Have I setup something wrong which is causing the incorrect constructor to be called?
or
Is this the default behaviour, and if so, how do I change it to ensure the desired constructor is called?
The Setup:
I have the following Registry class:
public class IocRegistry : Registry
{
public IocRegistry()
{
this.For(typeof(IRepository<>)).Use(typeof(MongoRepository<>))
.CtorDependency<string>("connectionString")
.Is("MongoServerSettings");
}
}
And the following Container initialization:
public static IContainer Initialize() {
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.LookForRegistries();
scan.WithDefaultConventions();
});
});
return ObjectFactory.Container;
}
The IocRegistry is definitely getting hit when I put a break-point in its constructor and debug the application.
The code which consumes the IRepository<> is:
public class ImageContentService : IImageContentService
{
private IRepository<ImageItem> imageRepository;
public ImageContentService(IRepository<ImageItem> imageRepository)
{
this.imageRepository = imageRepository;
}
}
The relevant output of ObjectFactory.Container.WhatDoIHave() (once all registrations have taken place) is:
===========================================================================================================
Configuration Sources:
0) Registry: StructureMap.InitializationExpression, StructureMap, Version=2.6.4.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
1) Registry: StructureMap.Configuration.DSL.Registry, StructureMap, Version=2.6.4.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
2) Registry: StructureMap.Configuration.DSL.Registry, StructureMap, Version=2.6.4.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
3) Registry: Mch.Core.IocRegistry, Mch.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
============================================================================================================================================================================================================================================================================================================================================
PluginType Name Description
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
IRepository`1<T> (IRepository`1<T>) d26c76dd-8550-4d02-bf8a-25c6f0075346 Configured Instance of MongoRepository.MongoRepository`1, MongoRepository.Net45, Version=1.6.2.0, Culture=neutral, PublicKeyToken=null
Scoped as: Transient
d26c76dd-8550-4d02-bf8a-25c6f0075346 Configured Instance of MongoRepository.MongoRepository`1, MongoRepository.Net45, Version=1.6.2.0, Culture=neutral, PublicKeyToken=null
You can tell StructureMap which constructor to use:
x.SelectConstructor<ClassWithTwoConstructors>(()=>new ClassWithTwoConstructors(0));
You can find this in the StructureMap documentation.
I had the exact same issue. In the end I solved it by configuring my MongoRepository in the ObjectFactory initialisation like so:
x.For<IRepository<T>>().Use(() => new MongoRepository<T>("{ConnectionString}","{CollectionName}"));
This way it seems the Structuremap seems to use the correct constructor. Obviosuly you can instantiate the MongoRepository with whichever constructor you prefer.
I have created a simple project using ASP.Net MVC template in Visual Studion 2013 Express for Web. It does not use any authentication. Then I installed EntityFramework (v6.0.1), EntityFramework.SqlServerCompact packages.
My DbContext class is very simple:
public class EditTestContext : DbContext
{
public EditTestContext() : base("EditTestContext")
{
}
public EditTestContext(string connectionString) : base(connectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(
new DropCreateDatabaseIfModelChanges<EditTestContext>());
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new EditTestConfig());
}
}
The actual context object is created in the Unit of Work class:
public class EditTestUoW:IEditTestUoW,IDisposable
{
private DbContext dbContext;
public EditTestUoW()
{
CreateDbContext();
}
private void CreateDbContext()
{
dbContext = new EditTestContext();//NEW DBCONTEXT OBJECT IS CREATED
dbContext.Configuration.LazyLoadingEnabled = false;
dbContext.Configuration.ProxyCreationEnabled = false;
dbContext.Configuration.ValidateOnSaveEnabled = false;
}
public IRepository<EditTestModel> EditTestRepo
{
get
{
return new EFRepository<EditTestModel>(dbContext);
}
}
}
The connection string being used is:
<add name="EditTestContext" connectionString="Data Source=
|DataDirectory|EditTestDb.sdf;Max Database Size=256;
Max Buffer Size=1024;File Mode=Shared Read;
Persist Security Info=False;" providerName="System.Data.SqlServerCe.4.0" />
Now when I try to access any thing using this Context like:
var rep=UoW.EditTestRepo;
var list=rep.GetAll().ToList();
I am getting following exception on rep.GetAll() function:
System.InvalidOperationException: Sequence contains no matching element
On digging deeper, IQueryable from Repository class (DbSet<EditTest>) is throwing following exception:
The context cannot be used while the model is being created. This exception may
be thrown if the context is used inside the OnModelCreating method or if the same
context instance is accessed by multiple threads concurrently. Note that instance
members of DbContext and related classes are not guaranteed to be thread safe.
I thought it might have been caused by ninject, but it is still there even after I removed it.
What I am doing wrong here or something (some assembly reference etc.) is missing?
Well after some other search on the issue, I got this MSDN forum link. As was suggested by Rowan, I tried to manually initialize the context using following statement in my EFRepository class:
dbContext.Database.Initialize(false);
The application failed way before it was hitting the GetAll() method. But this exposed the stack trace which gave me some direction:
[InvalidOperationException: Sequence contains no matching element]
System.Linq.Enumerable.Single(IEnumerable`1 source, Func`2 predicate) +2614017
System.Data.Entity.Utilities.DbProviderManifestExtensions.GetStoreTypeFromName
(DbProviderManifest providerManifest, String name) +146
.....Other Lines.....
System.Data.Entity.Internal.InternalContext.Initialize() +31
System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType
(Type entityType) +38
System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() +138
System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext() +38
System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable
.get_Provider() +99
System.Linq.Queryable.Any(IQueryable`1 source) +50
Then searching for DbProviderManifestExtensions.GetStoreTypeFromName revealed that this is the line where EF was trying to get column type. I had specified UNIQUEIDENTIFIER for my Id column:
Property(x=> x.Id).HasColumnType("UNIQUEIDENTIFIER")
Once I commented this, all was well.
Though there is a request on Codeplex to provide the proper error message in case the column type is not valid for database provider.
My Seed() method is never called. It is called when I do an Update-Database from the Package Manager Console, but never when I run from code.
If I delete my database, all tables are created ( so my migration classes are executed), but my Seed() code is never called.
MVC 4, Entity Frame Work 5 Code First.
Global.asax:
protected void Application_Start()
{
Database.SetInitializer<MyContext>(new DbInitializer());
}
DBInit:
internal class DbInitializer : MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration>
{
}
DBContext:
public partial class MyContext : DbContext
{
public MyContext() : base("DefaultConnection")
{
}
// public DBSets....
}
Configuration:
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
// The constructor is actually called
AutomaticMigrationsEnabled = false;
}
protected override void Seed(MyContext context)
{
// My seed code, never called
}
What could be wrong?
So the reason was that I needed to specify my custom Initializer in the config file:
<entityFramework>
<contexts>
<context type="EFTest2.MyContext, EFTest2">
<databaseInitializer type="EFTest2.Initializers.DbInitializer, EFTest2" />
</context>
</contexts>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
After that, my Seed method is called.
Please first refer to the accepted answer.
I just want to add a very important note to this issue.
I was facing EXACTLY the same problem which described by this question (and this lead me to here). BUT I was using CreateDatabaseIfNotExists instead of MigrateDatabaseToLatestVersion and my seed method was not executed even after applying the accepted answer.
My problem was the following :
According to the documentation of the for the Seed method :
the Seed method of the DbMigrationsConfiguration will not be executed if the Database Initializer is one of the following
DropCreateDatabaseAlways
DropCreateDatabaseIfModelChanges
CreateDatabaseIfNotExists
If you are using one of those types, you should create your own class which inherits from one of those types, and then override the seed method in your own class.
In my case, adding the following class solved the problem.
public class CreateNotifierDatabaseIfNotExists : CreateDatabaseIfNotExists<NotifierContext>
{
protected override void Seed(NotifierContext context)
{
// the code of the seeding is go here
}
}
In my experience, this can happen when you stop trying to access DataContext. We had a method that was throwing errors when DataContext was finished creating the DataBase. We commented it out in order to resolve the problems with DataBase creation.
The problem was, this was the only method that actually used DataContext on initial loading. That meant that no DbInitialization logic executed, because an instance wasn't yet required.
When we added a less-problematic data retrieval logic instead of the commented-out one, the DbCreation and Seeding logic took place as normal.
I am trying to incorporate 'EF Tracing Data Provider' into an existing MVC2 app using VS2010, .NET 4.0 in order to log all SQL commands. I have no interest at this time in the caching provider. I beleive I have followed all the steps listed in the blog posting. BLOG POST My project does compile without error, however when I attempt to run the project I get the following error:
'String cannot have zero length.' The error points to Extended_JCIMS_MVC2_EF_Entities.cs Line: 25
Line 25: public ExtendedJCIMS_DevEntities(string connectionString)
Line 26: :base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
I am unable to determine what is causing this error. I assume the error is referring to the connection string from the Web.Config file. It does not like the 'connectionString' variable. I'm obviously doing something worng. I would appreciate a push in the right direction.
The relevant bits are as follows:
Web.config
--------------------------------------------------------------------------
<add name="JCIMS_DevEntities"
connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=MyServer;Initial Catalog=MyDatabase;User ID=MyUser;Password=myPassWord;MultipleActiveResultSets=True""
providerName="System.Data.EntityClient"/>
<system.data>
<DbProviderFactories>
<add name="EF Tracing Data Provider" invariant="EFTracingProvider" description="Tracing Provider Wrapper"
type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
<add name="EF Generic Provider Wrapper" invariant="EFProviderWrapper" description="Generic Provider Wrapper"
type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
</DbProviderFactories>
</system.data>
Global.ascx
-----------------------------------------------------------------------
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
//EFTracingProviderConfiguration - LOG ALL Sql commands
EFTracingProviderConfiguration.LogToFile = Server.MapPath("~/JCIMS_MVC2_EF_SQL_Logfie.txt");
}
Extended_JCIMS_MVC2_EF_Entities.cs
--------------------------------------------------------------------------
namespace JCIMS_MVC2_EF.DomainModel
{
/// <summary>
/// Partial calss that Extends the EF Datacontext Class
/// </summary>
public partial class ExtendedJCIMS_DevEntities : JCIMS_DevEntities
{
private TextWriter logOutput;
public ExtendedJCIMS_DevEntities()
: this("name=JCIMS_DevEntities")
{
}
public ExtendedJCIMS_DevEntities(string connectionString)
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
connectionString,
"EFTracingProvider"
))
{
}
//... and more
}
}
SearchRepository.cs
------------------------------------------------------------------
public class SQLSearchRepository : ISearchRepository
{
//Database connection
private ExtendedJCIMS_DevEntities db = new ExtendedJCIMS_DevEntities(); // tracing version
public IEnumerable<SearchResults> ListAll(string strSearch, string chkSearch)
{
return (from s in db.Schools....
// and more...
}
Appreciate any assistance anyone can give me...
Have you debugged and confirmed that the connectionString passed into the ExtendedJCIMS_DevEntities method is not null or empty? That's what the error seems to indicate.