How connect to an existing Azure SQL database with ASP.NET CORE MVC using Entity Framework Core with migrations? - sql-server

I have an app using Azure App Services with SQL server and SQL database that are connect to my web app on asp MVC. I've used Distributed Sql Server Cache as a table on my database and so far everything is working well and connected to each other.
Now I want to do two things:
Add entity framework to my app (I already have the database and
connection string)
Run migration – after I've published my app (If I've added for a
example new line or new
table, now I have new version)
I'm not sure how to do those things , I've looked up on many guides and couldn't find an answer. I found a post similar to mine – but using azure functions - here
. I would appreciate it if someone can help me with the steps that I need to follow (like they did in that post) to get entity framework and the migration.
Here is my code:
Program.cs-
using Microsoft.Extensions.Azure;
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
if(!builder.Environment.IsDevelopment())
builder.Configuration.AddAzureKeyVault(new Uri(Environment.GetEnvironmentVariable("VaultUri")), new DefaultAzureCredential());
builder.Services.AddControllersWithViews();
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddBlobServiceClient(builder.Configuration["storage:blob"], preferMsi: true);
clientBuilder.AddQueueServiceClient(builder.Configuration["storage:queue"], preferMsi: true);
});
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("db");
options.SchemaName = "dbo";
options.TableName = "_Cache";
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
Home Controller:
namespace WebAppAzure.Controllers
{
public class HomeController : Controller
{
private readonly BlobServiceClient storage;
private readonly ILogger<HomeController> logger;
private readonly IDistributedCache cache;
public HomeController(BlobServiceClient storage, ILogger<HomeController> logger,
IDistributedCache cache)
{
this.storage = storage;
this.logger = logger;
this.cache = cache;
}
public IActionResult Index()
{
var containerClient = storage.GetBlobContainerClient("public");
var blob = containerClient.GetBlobClient("image.jpeg");
var model = blob.Uri.ToString();
return View(model: model);
}
public IActionResult Privacy()
{
var stringModel = DateTime.Now.ToString();
cache.SetString("name", stringModel);
return View(model: $"SET: {stringModel}");
}
public IActionResult About()
{
var stringModel = cache.GetString("name");
return View(model: $"GET: {stringModel}");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

Add entity framework to my app (I already have the database and connection string)
Use below code for add Entity framework and upload to azure app service and run migration command to migrate database.
DBcontext file in project.
using Microsoft.EntityFrameworkCore;
using WebApplication_72783922.Entity;
namespace WebApplication_72783922
{
public class DbConnectionEntity : DbContext
{
public DbConnectionEntity()
{
}
//string connectionString = Environment.GetEnvironmentVariable("ConnectionStrings:dbcon").ToString();
public DbConnectionEntity(DbContextOptions<DbConnectionEntity> options)
: base(options)
{
}
public virtual DbSet<Users> users { get; set; }
public virtual DbSet<department> Departments { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Server=xxxx;Initial Catalog=database;Persist Security Info=False;User ID=adminserver72783922;Password=xxxx;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
}
Program.cs File code.
using Microsoft.Extensions.Azure;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
var builder = WebApplication.CreateBuilder(args);
if (!builder.Environment.IsDevelopment())
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = "Server=xxxx;Initial Catalog=database;Persist Security Info=False;User ID=adminserver72783922;Password=xxxx;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
options.SchemaName = "dbo";
options.TableName = "_Cache";
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Run migration – after I’ve published my app
Enable Migration using this command on Package Manager Console enable-migrations
Then add-migration InitialCreate
Then create migrationadd-migration test-v1
update database update-database -verbose

Related

Blazor WASM Hosted Authentication Null Reference Exception in IdentityServerBuilderConfigurationExtension

I have a blazor WASM Hosted project using IdentityServer4 ( default from VS templates ). When I launch my application, however, I get the following error. Debugging shows that the options.Value.SigningCredential is null so the .Key is causing the NullReferenceException. So I am missing something, somewhere.
Here is the Client Program.cs
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
//builder.RootComponents.Add<App>("#app");
builder.Services.AddHttpClient("BBQFriend.API", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BBQFriend.API"));
builder.Services.AddApiAuthorization();
var baseAddress = new Uri("https://localhost:44395/api/");
void RegisterTypedClient<TClient, TImplementation>(Uri apiBaseUrl)
where TClient : class where TImplementation : class, TClient
{
builder.Services.AddHttpClient<TClient, TImplementation>(client =>
{
client.BaseAddress = apiBaseUrl;
});
}
RegisterTypedClient<ICountryService, CountryService>(baseAddress);
await builder.Build().RunAsync();
}
Here is the Server Startup.cs
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//Register EntityFramework Core Datacontext for Dependency Injection
services.AddDbContext<DataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//Add common Identity Screens
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<DataContext>();
//Set up IdentityServer
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, DataContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
//Register Services for DirectNavigation
services.AddScoped<ICountryService, CountryService>();
//Register Repositories for Dependency Injection
services.AddScoped<ICountryRepository, CountryRepository>();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddControllersWithViews();
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
//endpoints.MapFallbackToFile("index.html");
endpoints.MapFallbackToPage("/_Host");
});
}
And here is the applications DataContext.cs
public class DataContext : ApiAuthorizationDbContext<ApplicationUser>
{
public DataContext(DbContextOptions options, IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
public DbSet<Country> Countries { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new CountryConfiguration());
base.OnModelCreating(modelBuilder);
}
}
Issue is that you are missing the client config values.
As you are using builder.Services.AddApiAuthorization(); it tries to load configuration from default:
By default, configuration for the app is loaded by convention from _configuration/{client-id}. By convention, the client ID is set to the app's assembly name. This URL can be changed to point to a separate endpoint by calling the overload with options.

PostgreSQL and Blazor .Net Core 3.1

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;
}
}

How To Reduce Queries On Layout .NET Core

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>();

How can i set language from SQLServerDataSource using Spring data

Good I have the need to make certain queries to the database sql server working with dates, I am presented with the inconvenience that as language in the installation of sql server is in English therefore the results are defined in that language for example to perform the consult with the following function DATENAME (WEEKDAY, date) the result would be for example Tuesday, as far as my need is to show it in Spanish.
A solution that I have applied is to do this from the application, obtaining the date and perform the conversion from the application.
But I would like to know if it is possible to make this configuration globally from the application through the configuration parameters of the DataSource?
Configuration:
#Configuration
#EnableJpaRepositories(basePackages = { "com.company.app.repository" })
#ComponentScan(basePackages = { "com.company.app.repository" })
#EnableTransactionManagement
#EnableJpaAuditing
#PropertySource("classpath:/db.properties")
public class ApplicationConfig {
#Autowired
private Environment environment;
//private static final Logger LOGGER = LogManager.getLogger(ApplicationConfig.class);
#Bean
public DataSource dataSource() {
// OracleDataSource oracleDS = null;
SQLServerDataSource dataSource = null;
// oracleDS = new OracleDataSource();
dataSource = new SQLServerDataSource();
dataSource.setURL(environment.getProperty("cendb.url"));//
dataSource.setUser(environment.getProperty("cendb.user"));//
dataSource.setPassword(environment.getProperty("cendb.password"));
return dataSource;
}
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setShowSql(true);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.SQLServerDialect");
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.company.app.model");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
#Bean
public AuditorAware<String> createAuditorProvider() {
return new CustomAuditorAware();
}
#Bean
public AuditingEntityListener createAuditingListener() {
return new AuditingEntityListener();
}

Spring Data MongoDB Testing

I wrote a small study-project to play a little bit with mongodb. I was using Spring Data to get Mongo Repository. It was quite easy to create RestController and using MongoRepository through Service (another class) retrieve info from mongodb and render it to the browser.
public interface PersonRepository extends MongoRepository<Person, Integer> {
List<Person> findByName(String name);
#Query("{'name':{$regex:?0}}")
List<Person> findByNameLike(String nameLike);
//the rest of methods
}
Now I decided to test my business logic and created the following class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {AppConfig.class})
public class TestMongo {
private static final Logger LOG = LoggerFactory.getLogger(TestMongo.class);
#Autowired
private PersonRepository personRepository;
#Before
public void setUp() {
Technology technology1 = new Technology("Java-7");
Technology technology2 = new Technology("Java-8");
Technology technology3 = new Technology("Hibernate");
Technology technology4 = new Technology("MyBatis");
Technology technology5 = new Technology("Spring Data");
Project project1 = new Project(1, "POINT", Arrays.asList(technology1, technology3));
Project project2 = new Project(2, "Forecast", Arrays.asList(technology1, technology4));
Project project3 = new Project(3, "CPM", Arrays.asList(technology2, technology5));
Person person1 = new Person(1, "Alex", 27, Arrays.asList(project1, project3));
Person person2 = new Person(2, "Ivan", 26, Arrays.asList(project2, project3));
Person person3 = new Person(3, "Andrii", 31, Arrays.asList(project1));
personRepository.save(Arrays.asList(person1, person2, person3));
}
#Test
public void count() {
List<Person> all = personRepository.findAll();
LOG.info("There are " + all.size() + " person(s) in database");
assertThat(all.size(), equalTo(3));
}
#Test
public void findByName() {
List<Person> personList = personRepository.findByName("Ivan");
LOG.info("*******Find by name********");
LOG.info("personList {}", personList);
LOG.info("***************************");
assertThat(personList, hasSize(1));
}
//another test methods
#After
public void shutDown() {
personRepository.deleteAll();
}
}
where AppConfig.class looks as:
#Configuration
#EnableMongoRepositories
#ComponentScan
public class AppConfig {
#Bean
public MongoClient mongoClient() {
return new MongoClient("localhost", 27017);
}
#Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(),"my-mongo");
}
}
And now my problem: I don't actually want to run some tests on the same database where I store my data. Moreover, I have a crucial method personRepository.deleteAll() after which all the data will just vanish.
I found embedded mongodb as a solution but once I add it to my pom.xml I don't see my installed database anymore.
So, the question is whether it's possible to have both installed and embedded mongodb on the same machine and if not how to test my MongoRepository without modifying prod data.
It looks like I found what I was missing:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.5</version>
<scope>test</scope>
</dependency>
Simply adding scope test is launching my embedded mongodb for tests only, while I'm still using installed mongodb for prod.

Resources