Entity Framework tables are not generated using in memory SQLite - sqlite

We are trying to move to using an in-memory SQLite instance for our unit test automation, instead of SQL Server or SQL Express. We use Entity Framework Core.
I think I have everything configured correctly, but it's still failing, so I must be missing a step, but I'm not sure what it is.
In our test project's app.config, I've specified:
<connectionStrings>
<add name="BusinessDb" providerName="System.Data.SQLite.EF6" connectionString="data source=:memory:"/>
</connectionStrings>
Our production concrete class is a bit more complex (it has many more modelBuilder calls in the OnModelCreating() method and many more DbSet objects, but it is basically like this:
namespace Business.Base.Concrete
{
public class SqlBusinessDb
: DbContext
, IBusinessDb
{
public string ConnectionString { get; set; }
public SqlBusinessDb(string connectionString)
{
ConnectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
if (ConnectionString.Contains("memory"))
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlite(ConnectionString,
options =>
options.CommandTimeout(SqlSettings.s_CommandTimeoutInSec.CurrentValue)
.MigrationsHistoryTable("_BusinessDB_Migrations"))
.AddInterceptors(new Deals.Base.SQL.SqlPerfCounterInterceptor());
}
else
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(ConnectionString,
options =>
options.CommandTimeout(SqlSettings.s_CommandTimeoutInSec.CurrentValue)
.MigrationsHistoryTable("_BusinessDB_Migrations")
.EnableRetryOnFailure())
.AddInterceptors(new Deals.Base.SQL.SqlPerfCounterInterceptor());
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Has<BillingPlan>()
.HasManyToOne(p => p.Companies, a => a.BillingPlan, a => a.BillingPlan_Id)
}
public int ExecuteStoreCommand(string commandText, params object[] parameters)
{
return Database.ExecuteSqlRaw(commandText, parameters);
}
public DbSet<Features.FeatureOverride_Plan> FeaturesPlan { get; set; }
public DbSet<Business> Businesses { get; set; }
}
}
In our test project we call it like so:
public static TestBusinessDb GetInstance()
{
SqlBusinessDb realRepository = new SqlBusinessDb();
if (!_hasBeenMigrated)
{
_hasBeenMigrated = true;
DatabaseFacade dbf = realRepository.Database;
var issqlite = dbf.IsSqlite();
var tables = dbf.ExecuteSqlRaw("SELECT * FROM information_schema.tables;");
// for the Test Repository, we migrate once when we first try and connect.
realRepository.Database.Migrate();
}
}
This code fails on the "dbf.ExecuteSqlRaw()" line with:
Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: 'no such table: information_schema.tables'.
If I remove that line, it fails on: realRepository.Database.Migrate(); with
Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: 'no such table: _BusinessDB_Migrations'.
When debugging it successfully ran the OnConfiguring and OnModelCreating methods and I watched it execute a SQL command that created that table. dbf.ProviderName returns "Microsoft.EntityFrameworkCore.Sqlite". So, why aren't the tables being found? Is there something else that needs to be in place that I'm missing?

It turns out that SQLite is unable to handle migrations anyway, so it is not a viable option.

Related

How to design repository based on Entity Framework core?

I have created a Repository on top of Entity Framework Core, but have some issues with how it's done.
This is an example:
public class StockPricesRepository : IStockPricesRepository
{
StockPricesDbContext _stockPricesDbContext;
ILogger _logger;
public StockPricesRepository(StockPricesDbContext stockPricesDbContext, ILogger logger)
{
_stockPricesDbContext = stockPricesDbContext;
_logger = logger;
}
public void Add(StockPrice stockPrice)
{
_stockPricesDbContext.Add(stockPrice);
_stockPricesDbContext.SaveChanges();
}
public void AddOrUpdate(StockPrice stockPrice)
{
if (!Exists(stockPrice))
_stockPricesDbContext.Add(stockPrice);
else
_stockPricesDbContext.Update(stockPrice);
_stockPricesDbContext.SaveChanges();
}
private bool Exists(StockPrice stockPrice)
{
StockPrice existingStockPrice = Get(stockPrice.Ticker, stockPrice.Exchange, stockPrice.Date, stockPrice.DataProvider);
return (existingStockPrice != null);
}
public StockPrice Get(string ticker, string exchange, DateTime date, string providerName)
{
StockPrice stockPrice = null;
stockPrice =
(from sp in _stockPricesDbContext.StockPrices
where (
(sp.Ticker == ticker) &
(sp.Exchange == exchange) &
(sp.Date == date) &
(sp.DataProvider == providerName))
select sp).AsNoTracking().FirstOrDefault();
return stockPrice;
}
}
}
The StockPricesDbContext is injected into the constructur using dependency injection like this:
services.AddDbContext<StockPricesDbContext>(options => options.UseSqlServer(connectionString));
The problem with the current design is that I get some issues with multiple calls into the repository within the lifetime of the StockPricesDbContext object (even if I have AsNoTracking() on the Get method):
System.InvalidOperationException: 'The instance of entity type 'StockPrice' cannot be tracked because another instance with the same key value for {'Date', 'Ticker', 'Exchange', 'DataProvider'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
To avoid this problem I would maybe prefer to have each of the repository methods embedded in a using block like this:
using (var db = new StockPricesDbContext())
{
}
The problem with this approach is that I don't get the StockPricesDbContext from dependency injection and the connection string from AddDbContext is lost.
One workaround for that might be to get the connection string from the constructor of the repository like this:
private string _connectionString;
public StockPricesRepository(StockPricesDbContext stockPricesDbContext, ILogger logger)
{
_stockPricesDbContext = stockPricesDbContext;
_connectionString = stockPricesDbContext.Database.GetDbConnection().ConnectionString;
_logger = logger;
}
I would also need to add this constructor in StockPricesDbContext:
public StockPricesDbContext(string connectionString)
{
_connectionString = connectionString;
}
and this would be the OnConfiguring method:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
if (_connectionString != null)
optionsBuilder.UseSqlServer(_connectionString);
}
}
Then I could use it in the methods like this:
using (var db = new StockPricesDbContext(_connectionString))
{
}
It kind of works, but seems a bit "unclean".
Has anyone else come across a good pattern for repositories built on top of Entity Framework Core?
The common mistake that causes this error is having several async queries that have not been committed.
There is no problem saving multiple records in EF though.
There are multiple approaches to avoid your issue.
Using AddRange(),UpdateRange(),DeleteRange()
AddRange() can add list of objects to your db and you don't need to call .Add() more than once. Similarly you can update or delete a list of objects
EFCore.BulkExtensions
Bulk Extensions in EFCore
Both are extending DbContext with Bulk operations and have the same syntax call:
context.BulkInsert(stockPriceList);
context.BulkUpdate(stockPriceList);
context.BulkDelete(stockPriceList);
context.BulkInsertOrUpdate(stockPriceList);

How to get job's information (name, status, last run, etc) in SQL Server agent using c# and mvc?

I am a rookie with ASP.NET currently developing monitoring system for SQL Server, and still working on retrieving job's information so I can show them on the page.
I tried to use Mr. Alexey Zimarev's code this code
And I wrote it on my controller like this
public class JobsController : Controller
{
static readonly string SqlServer = #"DESKTOP-PQD9KKN";
private ApplicationDbContext _context;
public JobsController()
{
_context = new ApplicationDbContext();
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
}
[Route("Customers/MigrateJob")]
public ActionResult MigrateJob()
{
ServerConnection conn = new ServerConnection(SqlServer);
Server server = new Server(conn);
JobCollection jobs = server.JobServer.Jobs;
foreach(Job job in jobs)
{
var jobactivity = new JobActivity
{
Name = job.Name,
IsEnabled = job.IsEnabled
....so on
};
_context.JobActivities.Add(jobactivity);
}
return RedirectToAction("List", "Jobs");
}
public ActionResult List()
{
var jobactivities = _context.JobActivities.ToList();
return View(jobactivities);
}
}
My approach is to store the job's information from SQL Server Agent to my JobActivity table using MigrateJob(). The problem is the job's information hasn't stored in my table yet without any error messages.
My Tools:
VS 2017
SQL Server ver 13
Any help would be appreciated :)

DropCreateDataBaseAlways is not working when working with multiple db schemas with Entity Framework 6 Code First

After watching the "Enhancements to Code First Migrations: Using HasDefaultSchema and ContextKey for Multiple Model Support" section of Julie Lerman's PluralSite video, "Entity Framework 6: Ninija Edition-What's New in EF 6" (https://app.pluralsight.com/library/courses/entity-framework-6-ninja-edition-whats-new/table-of-contents), it seems there is a way to run multiple schemas under a single database in Entity Framwork 6 using Code First Migrations...
However, based on the video you still need to these package manager commands for each project that houses a separate context:
1. enable-migrations
2. add-migration [MIGRATION NAME]
3. update-database
This is fine and good if you actually care about maintaining migrations going forward, which is not a concern of mine.
What I'd like to do is have each of my Context's initializers set to DropCreateDatabaseAlways, and when I start up my client app (in this case, an MVC site), code first will create the database for the first context used, create the tables in with the correct schema for that context, and then create the tables for the rest of the contexts with the correct schema.
I don't mind if the whole database is dropped and recreated every time I hit F5.
What is happening now is the last context that is accessed in the client app is the only context tables that are created in the database... any contexts being accessed before the last get their tables blown away.
I am currently using two contexts, a Billing context and a Shipping context.
Here is my code:
My client app is an MVC website, and its HomeController's Index method looks like this:
public ActionResult Index()
{
List<Shipping.Customer>
List<Billing.Customer> billingCustomers;
using (var shippingContext = new Shipping.ShippingContext())
{
shippingCustomers = shippingContext.Customers.ToList();
}
using (var billingContext = new Billing.BillingContext())
{
billingCustomers = billingContext.Customers.ToList();
}
}
Here is my DbMigrationsConfigurationClass and ShippingContext class for the Shipping Context:
internal sealed class Configuration : DbMigrationsConfiguration<ShippingContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(ShippingContext context)
{
}
}
public class ShippingContext : DbContext
{
public ShippingContext() : base("MultipleModelDb")
{
}
static ShippingContext()
{
Database.SetInitializer(new ShippingContextInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("Shipping");
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
class ShippingContextInitializer : DropCreateDatabaseAlways<ShippingContext>
{
}
}
Likewise, here is the DbMigrationConfiguration class for the Billing Context and the BillingContext class:
internal sealed class Configuration : DbMigrationsConfiguration<BillingContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(BillingContext context)
{
}
}
public class BillingContext : DbContext
{
public BillingContext() : base("MultipleModelDb")
{
}
static BillingContext()
{
Database.SetInitializer(new BillingContextInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("Billing");
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
class BillingContextInitializer : DropCreateDatabaseAlways<BillingContext>
{
}
}
based on the order that the contexts are being called in the controller's action method, whichever context is accessed last is the only context that is created... the other context is wiped out.
I feel like what I'm trying to do is very simple, yet code first migrations, as well as trying to "shoehorn" Entity Framework to represent multiple contexts as separate schemas in the same physical database seems a bit "hacky"...
I'm not that versed with migrations to begin with, so what I'm trying to do might not make any sense at all.
Any feedback would be helpful.
Thanks,
Mike

Confusion about generation of database and seeding

I am using entity framework code first
and also I have data seeding code
Now when I run my application my database gets generated but is not seeded with my dummy data.
I have to run entity framework once more to get all data populated.
Any idea why and how to fix that so i do not have to run my app 2x to get database and data?
thnx
my context definition file is:
public class Context : DbContext
{
public DbSet<Task> Tasks { get; set; }
public DbSet<Agency> Agency { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
And here is my seeding file
public class Configuration : DbMigrationsConfiguration<Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(Context context)
{
GenerateTasks(context);
context.SaveChanges();
}
private static void GenerateTasks(Context context)
{
if (context.Task.Any()) return;
context.Task.Add(new Task() { Name = "Received" });
}
}
And hook to create database is:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
var context = new Context();
context.Database.Initialize(true);
If your app is an ASP.NET one and is a seperate assembly from the data layer, then you can, instead of configuring the initialization like you did, configure it directly in the web.config. Maybe this is the reason for your problem.
So if it's an ASP.NET app, you can try the following:
(1)
Comment this out:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
var context = new Context();
context.Database.Initialize(true);
(2)
In web.config, insert this right at the end before the closing /configuration tag:
<entityFramework>
<contexts>
<context type="**fully-qualified name of your context class**,
**assembly name of your context**">
<databaseInitializer type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[**fully-qualified name of your context class**, **assembly name of your context**],
[**fully-qualified configuration type**, **assembly name of your context**, Version=1.0.0.0, Culture=neutral]], EntityFramework"/>
</context>
</contexts>
where fully-qualified configuration type is the class with your migrations-configuration (something like [...]Context.Migrations.Configuration)
I use this configuration-approach for myself in my projects and it works well!
That's true. call context.Tasks.Find(1) and then it will hit the database.
EF code-first uses this trick to postpone every thing. this way application's startup time seems much faster. (but actually it's not!)

Dependency Injection Query

I'm starting a web application that contains the following projects:
Booking.Web
Booking.Services
Booking.DataObjects
Booking.Data
I'm using the repository pattern in my data project only. All services will be the same, no matter what happens. However, if a customer wants to use Access, it will use a different data repository than if the customer wants to use SQL Server.
I have StructureMap, and want to be able to do the following:
Web project is unaffected. It's a web forms application that will only know about the services project and the dataobjects project.
When a service is called, it will use StructureMap (by looking up the bootstrapper.cs file) to see which data repository to use.
An example of a services class is the error logging class:
public class ErrorLog : IErrorLog
{
ILogging logger;
public ErrorLog()
{
}
public ErrorLog(ILogging logger)
{
this.logger = logger;
}
public void AddToLog(string errorMessage)
{
try
{
AddToDatabaseLog(errorMessage);
}
catch (Exception ex)
{
AddToFileLog(ex.Message);
}
finally
{
AddToFileLog(errorMessage);
}
}
private void AddToDatabaseLog(string errorMessage)
{
ErrorObject error =
new ErrorObject
{
ErrorDateTime = DateTime.Now,
ErrorMessage = errorMessage
};
logger.Insert(error);
}
private void AddToFileLog(string errorMessage)
{
// TODO: Take this value from the web.config instead of hard coding it
TextWriter writer = new StreamWriter(#"E:\Work\Booking\Booking\Booking.Web\Logs\ErrorLog.txt", true);
writer.WriteLine(DateTime.Now.ToString() + " ---------- " + errorMessage);
writer.Close();
}
}
I want to be able to call this service from my web project, without defining which repository to use for the data access. My boostrapper.cs file in the services project is defined as:
public class Bootstrapper
{
public static void ConfigureStructureMap()
{
ObjectFactory.Initialize(x =>
{
x.AddRegistry(new ServiceRegistry());
}
);
}
public class ServiceRegistry : Registry
{
protected override void configure()
{
ForRequestedType<IErrorLog>().TheDefaultIsConcreteType<Booking.Services.Logging.ErrorLog>();
ForRequestedType<ILogging>().TheDefaultIsConcreteType<SqlServerLoggingProvider>();
}
}
}
What else do I need to get this to work? When I defined a test, the ILogger object was null.
Perhaps some details on how you are calling this code from a test would be useful.
My understanding is that you need to ensure that the ConfigureStructureMap call has been made early in the applications life (e.g. in the Global.asax in a web project).
After that you would be calling for instances of IErrorLog using something like:
IErrorLog log = StructureMap.ObjectFactory.GetNamedInstance<IErrorLog>();

Resources