EF Core in .NET Core library - .net-core

The main idea is to add a EF Core nuget package to a .NET Core library project and then use that library in a bunch of applications (e.g., ASP.NET Core, Win Service, Console App) without configuring EF in each of them. And, if possible, without adding EF packages to each of them.
I'm wondering if it's possible.
My current problem is that I can't create a database based on the model I have in the library project.
It seems I can't just select the library project in the Package Manager Console and run update-database. It wants me to implement 'IDesignTimeDbContextFactory'.
I'm using .NET Core 2.1. Would it help if I update it to the latest version?

As mentioned by the error, you need to implement IDesignTimeDbContextFactory which is part of the Microsoft.EntityFrameworkCore.Design package so go ahead and install that in your library. Then create a class that implements IDesignTimeDbContextFactory appropriately.
Since you created a .NET Core library, set that as your startup project.
Then in your Package Manager Console, select your library as the Default project and run update-database.

Yes, you can do this.
Make sure you have all the prerequisites installed.
Create a .NET Core Console app
Create a Core Class library for Entity Framework
Reference the Class library from the Console App
Scaffold your database, go to Tools > Package Manager Console
From the dropdown set your default project to your class library so it will scaffold there.
Run this in the console (database first approach): Scaffold-DbContext "Your connecting string here" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models/EF -f
Create a class to get your context
public class Context
{
// See all queries generated by EF in debug window
public static readonly LoggerFactory MyLoggerFactory
= new LoggerFactory(new[] { new DebugLoggerProvider((s, level) => level >= LogLevel.Trace) });
public static DF.Data.Models.EF.YourContext GetContext()
{
var optionsBuilder = new DbContextOptionsBuilder<DF.Data.Models.EF.YourContext>();
optionsBuilder.UseSqlServer(
"Your Connection String").UseLoggerFactory(MyLoggerFactory);
return new DF.Data.Models.EF.YourContext(optionsBuilder.Options);
}
public partial class YourContext : DbContext
{
public YourContext(DbContextOptions optionsBuilderOptions) : base(optionsBuilderOptions)
{
}
}
}
Create a Repository class to store your queries if you would like.
Note: When you scaffold the database again make sure you select the Class library project as the default project from the dropdown. Then set your other project back to the startup project.

Related

How to setup ElmahCore for a Console Application?

I want to log errors for a console application using Elmah.I've found ElmahCore and elmah.io.core but I don't know how to setup any of them on a console app.I'm using .net core.
ELMAH (the open source project) doesn't work with .NET Core. ElmahCore has a lot of dependencies to ASP.NET Core, but if you really wanted to, you could do something like this:
class Program
{
static void Main(string[] args)
{
var log = new MemoryErrorLog();
log.Log(new Error(new Exception()));
var errors = new List<ErrorLogEntry>();
var result = log.GetErrors(0, 10, errors);
Console.WriteLine(result);
Console.WriteLine(errors);
Console.ReadLine();
}
}
You can replace MemoryErrorLog with a target logger of your choice.
The package named elmah.io.core is a deprecated package from elmah.io. elmah.io is (among other things) a commercial cloud version of ELMAH, where you store all of your errors in the cloud (list of differences between ELMAH and elmah.io). elmah.io works with .NET core through either the Elmah.Io.Client NuGet package or using one of the integrations for popular logging frameworks like Serilog and NLog.
I wouldn't recommend you to use ElmahCore for logging in a console application. It is created for ASP.NET Core. There are much better options for logging from a console application, like the mentioned logging frameworks.

ASP .NET Boilerplate + MongoDb

I am using ASP.Net boilerplate framework + SQL Server 2016 in my project. Recently I have faced a challenge with migration from SQL Server to MongoDB. I have found that it is possible with ASP .NET boilerplate and installed required NuGet packages, however, due to the lack of documentation the only thing I have managed to do is to define respective RepositoryBase class:
public abstract class MyRepositoryBase<TEntity, TPrimaryKey> : MongoDbRepositoryBase<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
protected MyRepositoryBase(IMongoDatabaseProvider databaseProvider)
: base(databaseProvider)
{
}
}
As far as I understand, first of all, I need to define connection string somewhere now. And then populate the database with required basic data(which previously had been done by EF Core migrations). Obviously, EF Core in the new approach is obsolete so does that mean for my DbContext class that it is obsolete as well?
Actually, there are plenty of questions in relation to ASP .NET boilerplate and MongoDB integration, therefore my current post is actually a request for provision of some kind of example of the existing integration. Thank you in advance.
You can register your module by depending on it on your web module.
[DependsOn(typeof(YourMongoDbModule))]
public class YourWebModule : AbpModule
{
}
I think you have to register the repository with:
IocManager.Register(typeof(IMongoRepository<>), typeof(MongoRepository<>), Abp.Dependency.DependencyLifeStyle.Singleton);
You can refer this sample.
Look at this comment also.
Here is a framework which maps EF Core to Mongo DB.

Is there a universal config file for .Net Standard 2.0 Class Library?

I have a Class Library that I'm converting to a .Net Standard 2 class library in order to also use in ASP.Net Core 2.0 projects.
The library has always read from a config file items such as SMTP settings, connection strings etc.
In Web Projects it finds these values in web.config.
In Console/WinForms it finds these values in app.config.
Is there an equivalent config file for .Net Core 2.0 projects that "just works" like the previous examples?
I assume the answer is no, but looking for best way to handle this given the library is used across the organization, so maintaining backwards compatibility is important.
Turns out System.Configuration.ConfigurationManager was added back in .NETStandard 2.0.
Just pull it in from nuget and compile the .NETStandard 2.0 class library project.
Then, the library will work across projects using standard config files:
Net Core 2.0 projects use app.config
Web projects work from web.config
Console and Windows apps work with app.config
.Net Core revised configuration approach greatly.
You don't call ConfigurationManager.AppSettings["someSetting"] anymore whenever you need value for some setting. Instead you load configuration on application startup with ConfigurationBuilder. There could be multiple configuration sources (json or/and xml configuration file, environment variables, command line, Azure Key Vault, ...).
Then you build your configuration and pass strongly typed setting objects wrapped into IOption<T> to consuming classes.
Here is a basic idea of how it works:
// Application boostrapping
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("AppSettings.json");
var configuration = configurationBuilder.Build();
// IServiceCollection services
services.AddOptions();
services.Configure<SomeSettings>(configuration.GetSection("SomeSection"));
// Strongly typed settings
public class SomeSettings
{
public string SomeHost { get; set; }
public int SomePort { get; set; }
}
// Settings consumer
public class SomeClient : ISomeClient
{
public SomeClient(IOptions<SomeSettings> someSettings)
{
var host = someSettings.Value.SomeHost;
var port = someSettings.Value.SomePort;
}
}
// AppSettings.json
{
"SomeSection": {
"SomeHost": "localhost",
"SomePort": 25
}
}
For more details check article Configure an ASP.NET Core App.
I'm afraid that it will be difficult (trying to avoid word 'impossible') to maintain backward compatibility.

How to get appsettings from project root to IDesignTimeDbContextFactory implementation in ASP.NET Core 2.0 and EF Core 2.0

I am building an application in ASP.NET Core 2.0 and I am having problems with EntityFramework Migrations.
I have my DbContext in a separate project (SolutionName\ProjectNamePrefix.Data) and therefore I created an implementation for the IDesignTimeDbContextFactory interface.
I wanted to use different connection strings for different environments and I need appsettings.json for that.
So after a quick search I found that I can create a new IConfigurationRoot object inside the CreateDbContext function as shown here:
https://codingblast.com/entityframework-core-idesigntimedbcontextfactory/
I added that and then for testing, tried to run dotnet ef migrations list -c MyContext from the Data project root folder.
Then I got the following error:
The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'C:\dev\*SolutionName*\*ProjectNamePrefix*.Data\bin\Debug\netcoreapp2.0\appsettings.json'.
So, basically, I tried 3 options for getting the correct root path:
Directory.GetCurrentDirectory();
env.ContentRootPath; (IHostingEnvironment object, I found a way to get it here: https://github.com/aspnet/Home/issues/2194)
AppDomain.CurrentDomain.BaseDirectory;
and all of them returned the same ..\bin\debug\netcoreapp2.0\ path. When I run the Data project from VS, then the two first options give me the correct project root folder.
Is there a way to get the correct project content root folder?
Because when I added --verbose to the EF command, it logged out a row:
Using content root 'C:\dev\FitsMeIdentity\FitsMeIdentity.Data\'.
So I understand that EF somehow knows the project root but all the options mentioned above return the path for the already built application.
The only option I found that works is that I change Copy output to root folder to Copy always but found from here: https://www.benday.com/2017/02/17/ef-core-migrations-without-hard-coding-a-connection-string-using-idbcontextfactory/ that it's not a good idea.
At first I even thought about creating a Constructor for the IDesignTimeDbContextFactory implementation which gets IOptions as a parameter but that didn't work, had the same problem as explained here:
Injecting Env Conn String into .NET Core 2.0 w/EF Core DbContext in different class lib than Startup prj & implementing IDesignTimeDbContextFactory
A little late, but here is the solution for those who hate hard-coding connections strings:
internal class MigrationDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", false)
.Build();
string connectionString = configuration.GetConnectionString("DefaultConnection");
DbContextOptionsBuilder<AppDbContext> optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
optionsBuilder.UseMySql(connectionString,
ServerVersion.AutoDetect(connectionString),
mySqlOptions =>
mySqlOptions.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null));
return new AppDbContext(optionsBuilder.Options);
}
}
No. You can't do this, and more to the point: you're not supposed to do this. The whole entire point of IDesignTimeDbContextFactory is that it's a way to get a DbContext instance from in a context where there is no ASP.NET Core framework to work with, i.e. from a class library. If you're running migrations from an ASP.NET Core project, you don't need it, and if you're not, none of the configuration stuff is available.
Additionally, it's only to be used for development, hence the "DesignTime" part of the name. As a result, there's no need for stuff like switching between connection strings for different environments. Just hard-code the connection string as the docs detail.

MvvmCross - how do I access SQLite in a windows store background task?

I have a store app that uses the mvvmcross sqlite plugin (community edition). This app has a periodic background task that accesses the database to get data to be shown in a live tile. I can't see how I can get access to this database from the background task. I would like to use the mvvmcross sqlite plugin in the background task, but I don't see how to initialize the mvvmcross environment properly.
If you want to initialize the full MvvmCross framework including all of your app, then you'll need to run your Setup class.
In WinRT, this could be as simple as calling:
var setup = new Setup(null /*rootFrame*/);
setup.Initialize();
although it may require you to do a little work to:
Make sure your presenter does not use the null rootFrame
Provide some other means to create a UI thread dispatcher - currently MvxStoreViewDispatcher relies on .Dispatcher access - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.WindowsStore/Views/MvxStoreViewDispatcher.cs - to do this, you could override InitializeViewDispatcher with something like:
protected override void InitializeViewDispatcher()
{
if (_rootFrame != null)
{
base.InitializeViewDispatcher(); return;
}
var dispatcher = new NonMainThreadDispatcher();
Mvx.RegisterSingleton<IMvxMainThreadDispatcher>(dispatcher);
}
public class NonMainThreadDispatcher : MvxMainThreadDispatcher
{
public bool RequestMainThreadAction(Action action)
{
action();
}
}
If you want to initialize less functionality than the entire framework (e.g. for memory reasons) then you can also consider creating special Setup and App classes just for your background task.
Aside> This is similar to questions like these in Android - Using MvvmCross from content providers and activities and MvvmCross initialization
I was able to solve the problem in a straightforward way. Since the background task only needed the SQLite data service from the PCL core project, I did the following:
Included a reference to the Core project.
Added the nuget packages for MvvmCross and the SQLite community plugin.
Deleted all of the files and folders added when doing the mvvmcross install: Bootstrap/, Todo-Mvvmcross/, Views/, DebugTrace.cs, and Setup.cs.
There is a current limitation in the nuget installer that requires some additional edits to the project file to handle multiple store platforms (x86, ARM, and x64), see 'Cirrius.Mvvmcross.Community.Plugins.SQLite.WindowsStore needs platform-specific dlls for X86 and ARM' on Stack Overflow for details. Make sure you put the Choose statement after the default SQLite.WindowsStore reference and you need to leave the default reference in the project file. You will also need to adjust the HintPath based on the location/names of your references.
Initialized the SQLite data service by explicitly calling the factory and creating a new instance of the data service:
var factory = new MvxStoreSQLiteConnectionFactory();
IMyDataService repository = new MyDataService(factory);
I then have access to the data service with no other overhead associated with mvvmcross.

Resources