I'm trying to create EF Core 3.1 context with readonly application intent for connection redirection to replica database.
When context's .ctor are called, connection string in SqlServerOptionsExtension extension of DbContextOptions has no ApplicationIntent=ReadOnly option.
Example code:
Startup.cs
protected override void DoEndConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkSqlServer()
.AddDbContext<ReadOnlyContext>(options =>
{
options.UseSqlServer("...;ApplicationIntent=ReadOnly");
}, ServiceLifetime.Scoped);
}
ReadOnlyContext.cs
public class ReadOnlyContext : DbContext
{
protected MovedContext(DbContextOptions<MovedContext> options) : base(options)
{
}
}
I have tried to set up connection in OnConfiguring method and it worked as intended.
How i could pass readonly connection string to context upon creating it from Startup class?
Related
Am learning about data sync from API to WPF app. Got a demo from https://github.com/Azure/azure-mobile-apps/tree/main/samples. But I got into a problem that all the data inside the tables are collected on the call but I need to select specific data using Id. Tried a query etc all came to nothing. Please guide me
Thank you
PatientsController.cs
[Route("tables/Patients")]
public class PatientsController : TableController<Patients>
{
public PatientsController(AppDbContext context)
: base(new EntityTableRepository<Patients>(context))
{
}
}
AppDbContext.cs
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Patients> Patients => Set<Patients>();
}
Try to use the following code:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Patients> Patients {get;set;}
}
controller:
[Route("tables/Patients")]
public class PatientsController : TableController<Patients>
{
private readonly AppDbContext _context;
public PatientsController(AppDbContext context)
: base(new EntityTableRepository<Patients>(context))
{
_context=context;
}
public async Task<IActionResult> Index(int id){
var Patients=_context.Patients.FindAsync(id);
return View(Patients);
}
}
If you just need to get a record by Id, you use the URL https://mysite/tables/myTable/id - no search required - it will go directly to the entity you want.
If you need to limit what a user can see, you will need to implement an access control provider (which is an implementation of IAccessControlProvider). This provides three methods to limit what a user can see and do model updates for ensuring the write operations store the right thing. You can read more here: https://learn.microsoft.com/en-us/azure/developer/mobile-apps/azure-mobile-apps/howto/server/dotnet-core#configure-access-permissions
If it's something else, you'll need to be a little more specific. I hang out in the issues and discussions of the azure/azure-mobile-apps repo, so feel free to file a question there.
I have ASP.NET Core Web API. The app has three functionalities that connects to DB. EF DbContext, Hangfire and Serilog Logging. All 3 reads the connection string from appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Error"
}
},
"Serilog": {
"Using": [ "Serilog.Sinks.MSSqlServer"],
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "Server=serverip;Database=MyDb;Integrated Security=True;",
"tableName": "Logs",
"schemaName": "logging",
"autoCreateSqlTable": false
}
}
]
},
"ConnectionStrings": {
"DefaultConnection": "Server=serverip;Database=MyDb;Integrated Security=True;"
}
}
in Programs.cs I have configured Serilog that auto reads the connectionString from appsettings
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:30000")
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
if (!hostingContext.HostingEnvironment.IsLocal())
{
logging.ClearProviders();
}
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostingContext.Configuration)
.CreateLogger();
logging.AddSerilog();
});
in Startup.cs we have Hangfire and EF DbContext using the same connection value
public void ConfigureServices(IServiceCollection services)
{
//dbContext
var connection = configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<MyDBContext>(options => options.UseSqlServer(connection));
services.AddHangfire(config => config.UseSqlServerStorage(connection));
}
Serilog, has separate entry in appsettings for connection string but the value for the connection string is same.
Each funcationality has its own SQL schema. DbContext -> dbo schema, Hangfire->hangfire schema, Serilog ->logging schema
As per the documentation
A connection pool is created for each unique connection string. When a
pool is created, multiple connection objects are created and added to
the pool so that the minimum pool size requirement is satisfied.
Connections are added to the pool as needed, up to the maximum pool
size specified (100 is the default).
Since the value of the connection string is same, does that mean all these 3 funcationalities sharing the same connection pool?
UPDATE 1
The reason I asked this question because we were seeing error
System.InvalidOperationException: Timeout expired. The timeout period
elapsed prior to obtaining a connection from the pool. This may have
occurred because all pooled connections were in use and max pool size
was reached.
Here is my implementation of service. Since service is registered with Scope lifetime, the DI container should dispose service at the end of request and that should dispose the dbContext as well.
public interface IBaseService : IDisposable
{
}
public abstract class BaseService : IBaseService
{
private bool _disposed = false;
protected readonly MyDBContext _dbContext;
protected BaseService(MyDBContext dbContext)
{
_dbContext = dbContext;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
if (_dbContext != null)
{
_dbContext.Dispose();
}
// Free any other managed objects here.
}
// Free any unmanaged objects here.
_disposed = true;
}
}
public interface IOrderService : IBaseService
{
Task<Order> Create(Order order);
}
public class OrderService:BaseService,IOrderService
{
public OrderService():base(MyDBContext dbContext)
{
}
public async Task<Order> Create(Order order)
{
_dbContext.Orders.Add(order);
await _dbContext.SaveChangesAsync();
// at this point I am guessing the SQL connection will be closed by the DBContext
}
}
and in Startup.cs it service is regiered with Scope lifetime
services.AddScoped<IOrderService, OrderService>();
Are there any resources on how to use the current version of Blazor (3.1) and PostgreSQL?
I've tried writing the simplest code, just to see whether it connects to the database but I get this error message: System.Net.Dns:GetHostByName is not supported on this platform
a button click would activate this code:
async void connection()
{
var connString = "Host=Server1;Username=postgres;Password=pass;Database=BlazorData";
try
{
await using var conn = new NpgsqlConnection(connString);
await conn.OpenAsync();
errcheck = "success";
}
catch (Exception ex)
{
errcheck = ex.Message;
}
}
I explain how I use it with entity framework. It might help you.
in startup.cs, ConfigureServices method have this
services.AddEntityFrameworkNpgsql().AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
You need two packages to be installed through nuget
Npgsql.EntityFrameworkCore.PostgreSQL
Npgsql.EntityFrameworkCore.PostgreSQL.Design
in appsetting.json make sure you have setup connection string correctly, below one is mine. Host can be localhost if database is in the same machine as the database
"DefaultConnection": "Host=192.168.16.240;Port=5432;Username=postgres;Password=mypassword;Database=mydatabase;"
That's basically it.
then define a application db context with your tables
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<Room> Rooms { get; set; }
public DbSet<Meal> Meals { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
in package mangaer console
add-migration initial
update-database
you should see the tables created in PgAdmin.
and use your dbsets like usual c# lists. and remember to inject applicationdbcontext in the controllers you need it.
The error says you need to resolve the hostname by yourself. Either pass an IP address or use Dns.GetHostEntry
using System.Linq;
using System.Net;
using System.Net.Sockets;
...
async void connection()
{
var host = Dns.GetHostEntry("Server1");
var firstIpV4Address = host.AddressList.First(a => a.AddressFamily == AddressFamily.InterNetwork);
var connString = $"Host={firstIpV4Address};Username=postgres;Password=pass;Database=BlazorData";
try
{
await using var conn = new NpgsqlConnection(connString);
await conn.OpenAsync();
errcheck = "success";
}
catch (Exception ex)
{
errcheck = ex.Message;
}
}
I created a class for getting my settings of website. In this settings table I'm storing phone number, email adress, address etc. Admin user can change their settings.
public interface IAppGlobalVariableService
{
Task<List<Configuration>> GetAllConfig();
Task<string> GetValue(string name);
}
public class AppGlobalVariablesService : IAppGlobalVariableService
{
private readonly IRepository<Configuration> _configRepository;
private static Task<List<Configuration>> _configList;
public AppGlobalVariablesService(IRepository<Configuration> configRepository)
{
_configRepository = configRepository;
_configList = GetAllConfig();
}
public async Task<List<Configuration>> GetAllConfig()
{
return await _configRepository.GetAll().ToListAsync();
}
public Task<string> GetValue(string name)
{
return GetConfigByName(name);
}
private static async Task<string> GetConfigByName(string name)
{
var configList = await _configList;
return configList.FirstOrDefault(x => x.ConfigName == name)?.ConfigValue;
}
}
In layout, I'm calling my settings like view components style.
#await AppConfig.GetValue("Facebook")
I injected _ViewImports
#inject IAppGlobalVariableService AppConfig
I will use this code most of place. Do you have any idea to reduce queries for dotnet core 2.2 ? Can I do something like querying database once to get variables. And store cache ?
UPDATE:
I updated my startup.cs file (AddTransient to AddScoped) It redureced queries. But I want to learn can I use single query ?
services.AddScoped<IAppGlobalVariableService, AppGlobalVariablesService>();
When I was making update my database by using db-migration, I faced a problem that was
Automatic migration was not applied because it would result in data loss.
(I used System.ComponentModel.DataAnnotations such as [Required] and [StringLength(25)] for some properties. For example Title property.)
I know if I set the AutomaticMigrationDataLossAllowed to true and Update-Database -Force, my database will be update but my data will be removed and I'm going to prevent from it. I want to protect my data.
I've used Entity Framework 6.x
How can I solve this problem?
Configuration class:
namespace Jahan.Blog.Web.Mvc.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration
: DbMigrationsConfiguration<Jahan.Blog.Web.Mvc.Models.JahanBlogDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
}
protected override void Seed(Jahan.Blog.Web.Mvc.Models.JahanBlogDbContext context)
{
}
}
}
Initial class:
namespace Jahan.Blog.Web.Mvc.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class Initial : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
}
My DbContext:
namespace Jahan.Blog.DataAccess
{
public class JahanBlogDbContext : IdentityDbContext<User, Role, int, UserLogin, UserRole, UserClaim>
{
public JahanBlogDbContext()
: base("name=JahanBlogDbConnectionString")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Comment>().HasRequired(t => t.Article).WithMany(t => t.Comments).HasForeignKey(d => d.ArticleId).WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().ToTable("User");
modelBuilder.Entity<Role>().ToTable("Role");
modelBuilder.Entity<UserRole>().ToTable("UserRole");
modelBuilder.Entity<UserLogin>().ToTable("UserLogin");
modelBuilder.Entity<UserClaim>().ToTable("UserClaim");
}
// ... codes ....
}
}
You can add sql to fix the data in a way that is acceptable to you. You need to ensure that the alter statements generated BY EF do not cause data loss.
Use the Sql method in a migration to run your own sql:
public override void Up()
{
//Add this to your migration...
Sql("UPDATE dbo.Table SET Name = LEFT(Name, 25) WHERE LEN(Name) > 25")
//...before the code generated by EF
AlterColumn("dbo.Table", "Name ", c => c.String(nullable: false, maxLength: 25));
}