ApplicationDbContext security - c#

I've added connection string in OnConfiguring. It working well But I do not know is it secure or not
My question is, Is this code safe?
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
var MyConfig = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
var connection = MyConfig.GetValue<string>("ConnectionStrings:DefaultConnection");
optionsBuilder.UseSqlServer(connection);
}
}

Related

Write a test for SQL Server using EF in C#

I'm trying to test the below function using Nunit:
public static void Create(Contact contact)
{
using (var db = new PhonebookContext())
{
db.Database.EnsureCreated();
try
{
db.Contacts.Add(contact);
db.SaveChanges();
Console.WriteLine($"Successfully added {contact.Name}!");
}
catch
{
Console.WriteLine(Helpers.CreateErrorMessage, contact.Name);
}
}
}
This is my current test:
[Test]
public void Create_Contact_DbRowIsAdded()
{
var contact = new Contact { Name = "Abhinav", PhoneNumber = 1234567890, };
SqlAccess.Create(contact);
Assert.Equals(contact, SqlAccess.GetLastContact());
}
The test fails with the following error:
System.InvalidOperationException : No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
If it helps, here's the code for the dbContext (which I believe the test has no access to):
public class PhonebookContext : DbContext
{
public DbSet<Contact> Contacts => Set<Contact>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
try
{
optionsBuilder.UseSqlServer(System.Configuration
.ConfigurationManager
.ConnectionStrings["SQLServer"]
.ConnectionString);
}
catch (Exception ex)
{
Console.WriteLine("An unknown error occurred while creating the database. Please make sure SQL server is running.");
Console.WriteLine(ex.Message);
}
}
}
The function works perfectly, except when it is put through the test.
Am I missing something?
That's because you do not provide in your's PhonebookContext the db configuration for Contact class (which is db entry i guess).
So in order to fix that you should do something simmilar:
internal class ContactDbConfiguration : IEntityTypeConfiguration<Contact>
{
public void Configure(EntityTypeBuilder<TaskDto> builder)
{
// register your properties of contact class here for example:
builder.HasKey(t => t.Id);
}
}
And please add on model creating method in your PhonebookContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new ContactDbConfiguration());
base.OnModelCreating(modelBuilder);
}
Finally your dbContext should be like this:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// your code here
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new ContactDbConfiguration());
base.OnModelCreating(modelBuilder);
}
public DbSet<Contact> Contacts { get; set; }

How to unlock the Sqlite database file in EntityFramework.Core

I have a project that uses Microsoft.EntityFrameworkCore.Sqlite 6.0.5 on net6.0. I am expecting the Sqlite database file to be unlocked after the DbContext has been disposed.
However, the behaviour I am observing is that the Sqlite database remains locked after the DbContext has been disposed and finalised. There is a project that reproduces the behaviour here.
How can I unlock the database file?
My DbContext looks like this:
public class MyContext : DbContext
{
~MyContext()
{
Console.WriteLine("Finaliser was called.");
}
public override void Dispose()
{
base.Dispose();
Console.WriteLine("Dispose was called.");
}
public static readonly string DbFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "_temp.db");
public DbSet<Foo> Summaries { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Foo>().HasKey(nameof(Foo.Id));
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlite($"Data Source={DbFile}");
}
}
I am using it like this:
public static void AddItem()
{
using var ctx = new MyContext();
ctx.Database.EnsureCreated();
ctx.Summaries.Add(new Foo {Bar = "Foo"});
ctx.SaveChanges();
}
ClearAllPools() or specify no pooling in connection string (Pooling=false)
https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/connection-strings#pooling

Migration with dotnet ef error: Unable to create an object of type 'MyClass'

I'm trying to have a migration using dotnet ef migrations add MyMigration, but shell returns an error: Unable to create an object of type 'AuthDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728.
This is my AuthDbContext.cs file:
using Auth.Data.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Auth.Data
{
public class AuthDbContext : IdentityDbContext<User>
{
private readonly string connStr;
//public DbSet<User> Users { get; set; }
public AuthDbContext(DbContextOptions<AuthDbContext> options) : base(options) //string connStr)
{
//this.connStr = connStr;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// string connStr = "";
// connStr = "";
// if (!optionsBuilder.IsConfigured)
// {
// optionsBuilder
// .EnableSensitiveDataLogging(true)
// .UseSqlServer(connStr);
// }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("auth");
//modelBuilder.Entity<User>().HasKey(p => new { p.User_id });
base.OnModelCreating(modelBuilder);
}
}
}
Can somebody help me to understand? Thanks.
You forgot to add this line
base.OnConfiguring(optionsBuilder);
EF Core can't finish context instantiating.

.Net Core 3 / Entity Framework Core putting connection string in Appsettings.json not working

Still new to .NET Core but I'm trying to put my connection string in the Appsettings.json file. I'm following the Microsoft document on how to do so and I think I have all my settings correct.
The project will compile and run, but when I hit the API I get an error
No database provider has been configured for this DbContext.
I used the connection string that EF Core put in my context and just commented it out so there is effectively nothing in the DbContext's OnConfiguring. This is connecting to an Oracle database.
ModelContext.cs
public partial class ModelContext : DbContext
{
public ModelContext()
{
}
public ModelContext(DbContextOptions<ModelContext> options)
: base(options)
{
}
public virtual DbSet<Customer> Customer{ get; set; }
public virtual DbSet<HubToken> HubToken{ get; set; }
public virtual DbSet<Token> Token { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//if (!optionsBuilder.IsConfigured)
//{
// //optionsBuilder.UseOracle("user id=myusername;password=mypassword;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=myserver)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=*****)))");
//}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Token>().HasNoKey();
modelBuilder.HasAnnotation("Relational:DefaultSchema", "MySCHEMA");
}
Appsettings.json
"ConnectionStrings": {
"DbConnection": "user id=myusername;password=mypassword;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=servername)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=***)))"
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<ModelContext>(options => {
options.UseOracle(Configuration.GetConnectionString("DbConnection"));
});
}
UPDATE
My code where the ModelContext is being called. I initially used the commented out "using" statement where I would just create the context. That's how I do it in .Net Framework but Core seems to be different. Then with some suggestions I tried the using statement trying to pass in the connection string in to the ModelContext but it doesn't like that either because the connection string is not available at that point. I've tried getting the connectionstring first before passing it to the ModelContext but something about that didn't work either, I just don't remember what it was since I've tried several things. I will say that at least at the moment it seems it was much simpler in EF for .Net. The DbContext would take care of getting the connection string from the app.config or web.config for me. Now it seems to be more of an effort to do that in Core but it's probably just because I'm so new withe the way Core works.
//using (var ctx = new ModelContext())
using (var ctx = new ModelContext(configuration.GetConnectionString("DbConnection")))
{
//Run the query and see if there are any results returned.
var results = ctx.Token.FromSqlRaw(sbQuery.ToString()).ToList();
if (results == null || results.Count == 0)
{
_logger.LogInformation("Api-Token not found in database. Access denied. Customer: {0} | Token: {1}", customer.ToString(), token.ToString());
context.Result = new BadRequestObjectResult("Access Denied. Invalid Token");
return;
}
if (!context.ModelState.IsValid)
{
_logger.LogInformation("Model is in-valid.");
context.Result = new BadRequestObjectResult(context.ModelState);
}
_logger.LogInformation("Api-Token is valid.");
return;
}
After more researching and playing around I finally got it to work. I'm posting my update so that it will hopefully help someone else down the road.
Added my connectionstring to the appsettings.json file
"ConnectionStrings": {
"DbConnection": "user id=myusername;password=mypassword;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=servername)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=***)))"
}
Then add to the context info to the ConfigurationServices in the Startup.cs file. I believe this adds the information to the Dependency Injection container. I'm using Oracle but you just change to options.UseSqlServer(connstring); if needed. You can verify the correct connectionstring info by putting a break point to see it in debug mode.
public void ConfigureServices(IServiceCollection services)
{
//setting connstring separately here just to be able confirm correct value.
var connstring = Configuration.GetConnectionString("DbConnection");
services.AddDbContext<ModelContext>(options =>
{
options.UseOracle(connstring);
});
}
In your Controller/Class add the context to a page property and use the class constructor to get the DbContext from the DI container. Then use the using statement and reference the local property that has the connectionstring information.
public class TokenAuthorizationFilter : IAuthorizationFilter
{
private readonly ModelContext _context;
private readonly ILogger<TokenAuthorizationFilter> _logger;
public TokenAuthorizationFilter(ILogger<TokenAuthorizationFilter> logger, ModelContext context)
{
_logger = logger;
_context = context;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
_logger.LogInformation("Authorizing Api-Token.");
//Get the values from the Request Header
context.HttpContext.Request.Headers.TryGetValue("Api-Token", out var token);
context.HttpContext.Request.Headers.TryGetValue("Customer", out var customer);
var sbQuery = new System.Text.StringBuilder();
sbQuery.Append("select * from YourUserTable ");
sbQuery.Append("where username=customer and password=token");
using (_context)
{
//Run the query and see if there are any results returned.
var results = _context.Token.FromSqlRaw(sbQuery.ToString()).ToList();
if (results == null || results.Count == 0)
{
_logger.LogInformation("Api-Token not found in database. Access denied. Customer: {0} | Token: {1}", customer.ToString(), token.ToString());
context.Result = new BadRequestObjectResult("Access Denied. Invalid Token");
return;
}
if (!context.ModelState.IsValid)
{
_logger.LogInformation("Model is in-valid.");
context.Result = new BadRequestObjectResult(context.ModelState);
}
_logger.LogInformation("Api-Token is valid.");
return;
}
}
}
You can use GetSection method from Configuration class to get your connection string
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<ModelContext>(options => {
options.UseOracle(Configuration.GetSection("ConnectionStrings")["DbConnection"]);
});
}

Getting SQL Server error when adding data to database using ASP.NET Web API

I am trying to add some data to a ASP.NET Web API from the same solution, but somehow I am getting this error from SQL Server.
This is my context
public class SampleCtxt: DbContext
{
public DbSet<TodoItem> TodoItems { get; set; }
public SampleCtxt(DbContextOptions<SampleCtxt> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=.\SQLEXPRESS;Database=APITESTDB; Initial Catalog=APITestDb; Trusted_Connection=True;");
}
}
Configure services method from API
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SampleCtxt>(opt =>
Catalog=master;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False; Database = APITESTDB;"));
opt.UseSqlServer(
Configuration
.GetConnectionString("DefaultConnection")));
services.AddControllers();
}
Connection string from json
"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=APITESTDB; Initial Catalog=APITestDb Trusted_Connection=True;"
},
Adding data from another console project
static void Main(string[] args)
{
using (SampleCtxt ctxt = new SampleCtxt(
new Microsoft.EntityFrameworkCore.DbContextOptionsBuilder<SampleCtxt>().Options))
{
TodoItem todoItem = new TodoItem() { Name = "qualquer" };
ctxt.TodoItems.Add(todoItem);
ctxt.SaveChanges();
}
}
Everything seems fine but I am getting this error:
Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)
Its seens that the ConnectionString was wrong and the instantiation of the class context, I solved the problem by adding a parameterless constructor and by correcting the OnConfiguring Method
public class SampleCtxt: DbContext
{
public DbSet<TodoItem> TodoItems { get; set; }
public SampleCtxt()
{
}
public SampleCtxt(DbContextOptions<SampleCtxt> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=APITESTDB;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;");
}
}

Categories

Resources