.net 6 and Redis cache integration using sets , hashes - asp.net-core-webapi

.net 6 and Redis cache integration using sets , hashes.
Please provide source code for this.
Use latest stack exchange library.

Here is a simple save demo, you can take a look:
Program.cs:
builder.Services.AddStackExchangeRedisCache(options => {
options.Configuration = "127.0.0.1:6379";
});
builder.Services.AddSession();
app.UseSession();
Controller:
[ApiController]
public class TestController : Controller
{
private readonly IDistributedCache _distributedCache;
public TestController(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
[HttpGet]
[Route("test")]
public void Test()
{
const string key = "message";
const string value = "hello";
_distributedCache.SetString(key, value);
}
}
Result:
For more examples, you can refer to this article:
Distributed caching in ASP.NET Core

Related

How to distinguish between multiple databases with the same structure and same context in the URL for an ASP.Net Core Web API

I'm trying to make a web api in asp.net core which can target multiple databases on the same server with the exact same structure. As such they should be able to share the same context and controllers. Making separate contexts and controllers for each database would quickly get out of hand and make it a pain to maintain. As such I should be able to do this in Startup.cs.
services.AddDbContext<DatabaseContext>(opt => opt.UseMySQL(Configuration.GetConnectionString("db1")));
services.AddDbContext<DatabaseContext>(opt => opt.UseMySQL(Configuration.GetConnectionString("db2")));
services.AddDbContext<DatabaseContext>(opt => opt.UseMySQL(Configuration.GetConnectionString("db3")));
The problem I'm running into is how I can distinguish which database is being queried. Ideally I would like to have it such that the url can be ...{database}[controller]... where the database parameter can be used to determine which database is being queried. I've tried looking into the multi-tenant approach however I can't work out how to adapt it to my specific use case.
Any help would be greatly appreciated.
Take a look at this:
https://entityframeworkcore.com/knowledge-base/58123230/connect-multiple-databases-to--net-core-project-via-entity-framework-core
You can do it with multiple DB Contexts from inheriting the principal DB Context where you define the DbSets.
Create base context and including all settings into this, DBSET:
public abstract class BaseContext : DbContext
{
public BaseContext(DbContext options)
: base(options)
{ }
public DbSet<object> FirstDbSet { get; set; }
...
}
inherit from BaseContext for both DBs(Databases):
public class NavaContext : BaseContext
{
public NavaContext (DbContext<NavaContext> options) : base(options)
{
}
}
public class StackContext : BaseContext
{
public StackContext(DbContext<StackContext> options) : base(options)
{
}
}
and register both in Startup.cs:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<NavaContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LATAMConnectionString")));
services.AddDbContext<StackContext>(options => options.UseSqlServer(Configuration.GetConnectionString("EUConnectionString")));
// Autofac
var builder = new ContainerBuilder();
// needed only if you plan to inject ICollection<BaseContext>
builder.RegisterType<NavaContext>().As<BaseContext>();
builder.RegisterType<StackContext>().As<BaseContext>();
builder.Populate(services);
return new AutofacServiceProvider(builder.Build());
}
add connection strings in appsettings.json:
"ConnectionStrings": {
"NavaConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true",
"StackConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
and now you can inject both contexts:
public class ReportRepository : IReportRepository
{
private readonly NavaContext latamDbContext;
private readonly StackContext euDbContext;
public ReportRepository(NavaContext latamDbContext, StackContext euDbContext)
{
this.latamDbContext = latamDbContext;
this.euDbContext = euDbContext;
}
}
or if you plan to inject collection of contexts:
public class ReportRepository : IReportRepository
{
private readonly ICollection<BaseContext> dbContexts;
public ReportRepository(ICollection<BaseContext> dbContexts)
{
this.dbContexts = dbContexts;
}
}
to access specific context:
var _stackContext= dbContexts.FirstOrDefault(x => x is StackContext) as StackContext;
var _navaContext= dbContexts.FirstOrDefault(x => x is NavaContext) as NavaContext;
The best way to create connection when there are multiple databases in dot net core is with IDesignTimeDbContextFactory
public class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
public BloggingContext CreateDbContext(string[] args)
{
// logic to determine which connection string to use
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("connectionString");
return new BloggingContext(optionsBuilder.Options);
}
}
Inject BloggingContextFactory using register services and pass args[] for logic to use connection string

Injecting dependency from a Class Library project to Xamarin Forms

I have got a brand new Xamarin Form project that requires access to an existing class in a class library project (net standards 2.1).
I would like Xamarin forms to use ClientQueries class from the other project.
ClientQueries has got a HttpClientFactory property using dependency injection and several methods to call an API and it looks something like this:
public class ClientQueries
{
private readonly ClientFactory _ClientFactory;
public TSClientQueries(ClientFactory ClientFactory)
{
_ClientFactory = ClientFactory ?? throw new ArgumentNullException(nameof(ClientFactory));
}
public async Task<Result<Token>> GetToken(CancellationToken cancellationToken, string username, string password)
{
var client = _ClientFactory.Create();
var response = await client.GetToken(cancellationToken, username, password).ConfigureAwait(true);
return response;
}
}
I follow this sample which explains we can make use of Microsoft.Extensions for HttpClientFactory and adding singleton services. This is the link: ASP.NET Core's Dependency Injection into Xamarin Apps with HostBuilder
I tried this StartUp class in Xamarin Forms project which is similar to the asp.net core project which uses the same Class Library with ClientQueries:
public class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
var a = Assembly.GetExecutingAssembly();
using var stream = a.GetManifestResourceStream("App1.appsettings.json");
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
c.AddCommandLine(new string[] { $"ContentRoot={FileSystem.AppDataDirectory}" });
c.AddJsonStream(stream);
})
.ConfigureServices((c, x) => ConfigureServices(c, x))
.ConfigureLogging(l => l.AddConsole(o =>
{
//o.DisableColors = true;
}))
.Build();
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
#region "api service"
services.AddSingleton<ClientQueries>();
services.AddHttpClient<Client>("HttpClient",
x => { x.BaseAddress = new Uri(ctx.Configuration["APIConfiguration:BaseAddress"]); }
).AddPolicyHandler(GetRetryPolicy());
services.AddSingleton<ClientFactory>();
#endregion
}
Is it possible to inject ClientQueries in ViewModels like the way I used to do it in asp.net core controllers?
public class AccountController : Controller
{
private readonly ClientQueries _ClientQueries;
public AccountController(ClientQueries ClientQueries)
{
_tsClientQueries = tsClientQueries;
}
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var t = await _ClientQueries.GetToken(CancellationToken.None, model.UserName, model.Password);
[coded abbreviated for simplicity]
Or Has Prism got anything functionality which will allow me to use depedency injection for HttpClientFactory in ClientQueries and Use ClienQueries as a singleton in ViewModels in Xamarin?
I tried DependencyServices and I did not get it right.
Thanks.

Get transistent/scoped Database access in singletonservice

i updating my app from asp core 1.0 to 2.0. In 1.0 i have a soulution for my longlive import-task, initialated as singleton. The singleton used the DBContext. But in core 2.0 this soulution dosn't work. Can you help me?
My soulution in aps core 1.0 was
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LocalConnection")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IDataStore, DataStore>();
services.AddSingleton<IImportRepository, ImportRepository>();
with
public class ImportRepository : IImportRepository
{
Importer Importer;
private readonly ApplicationDbContext DBContext;
private readonly IDataStore store;
private ImportSet runningSet = null;
public ImportRepository(ApplicationDbContext context, IDataStore store)
{
this.DBContext = context;
this.store = store;
Importer = new Importer(DBContext, store);
}
With this soulutions i get errormessages (in german, but i try to translate). "you cannot use scoped services in singleton"
Last attempt i used this solution
services.AddSingleton<ImportService>(
provider => new ImportService((ApplicationDbContext)provider.GetService(typeof(ApplicationDbContext)))
);
But here i get the errormessage "Cannot resolve scoped service 'Portal.Data.ApplicationDbContext' from root provider."
How can i get access to my database in my Import-Service?
You may resolve dependencies manually using IServiceProvider instance.
public class ImportRepository : IImportRepository
{
private readonly IServiceProvider _provider;
public ImportRepository(IServiceProvider provider)
{
_provider = provider;
...
}
public void DoSomething()
{
var dBContext = (ApplicationDbContext) provider.GetService(typeof(ApplicationDbContext));
...
}
}
By the way, there is an extension method GetService<T>(); defined in Microsoft.Extensions.DependencyInjection namespace:
// using Microsoft.Extensions.DependencyInjection;
var dBContext = provider.GetService<ApplicationDbContext>();
Since your singleton lives longer and is shared, the only option I see is that you take it as a parameter to the functions.
public class ImportRepository : IImportRepository
{
public void DoSomething(ApplicationDbContext context, IDataStore store)
{
}
}
The other option is to make ImportRepository scoped as well.
Ok. I have a soulution, that works, but not perfektly.
Like Juunas example i build a long life funktion
public async Task RunImportAsync(string fileName, DataService data)
{
await Task.Run(() =>
{
if (!System.IO.File.Exists(internalPath + fileName))
{
throw new Exception($"Datei {fileName} nicht gefunden.");
}
[long Operations...]
data.DBContext.Add(new ImportHistory(set));
data.DBContext.SaveChanges();
});
}
the call is simple
[HttpPost]
[Route("runImport")]
public async Task<IActionResult> RunImport([FromBody]dynamic body)
{
string id = "";
try
{
id = body.filename;
_logger.LogInformation($"Import from {id}");
await ImportService.RunImportAsync(id, DB);
return StatusCode(StatusCodes.Success_2xx.OK);
}
catch (Exception e)
{
return SendError(e);
}
}
But postmen get no Response with this solution. Is there a idea, how i can fix it?

ASP.net Core 2.0 cant find services.AddSession();

How can I use sessions in ASP.net Core 2.0? Im using HttpContext.Session.GetString(); But that does not save to the session because I need to configure Startup.cs so that I can use session.
You need to add the Microsoft.AspNetCore.Session Nuget package and then add support for sessions in ConfigureServices method:
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.CookieHttpOnly = true;
});
See more here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state.
You need to add sessions as a service in startup.cs file.
Remember to add it before services.AddMvc(); Go to this link for all the details.
Since there is no project.json in visual studio 2017, you can skip that part.
You need to register Session in service configuration -
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.CookieHttpOnly = true;
});
To use it anywhere inject in the constructor. Here I am using a controller constructor for demonstration -
using Microsoft.AspNetCore.Http;
public class AccountController : Controller
{
private ISession _session;
public AccountController( IHttpContextAccessor contextAccessor)
{
_session = contextAccessor.HttpContext.Session;
}
}
Now to Get and Set value in session to simplify my purpose; I used an extension method below -
public static class HttpSessionExtension
{
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default(T) :
JsonConvert.DeserializeObject<T>(value);
}
}
The usage is simple -
_session.Set<MyModel>(Mykey, value);
_session.Get<MyModel>(Mykey);

ExpressMapper, .NET Core, and Dependency Injection

I'm evaluating different object-to-object mappers for a .NET Core web API. One of the requirements I have is to use the built-in Dependency Injection container of .NET Core. I have tested Automapper, which is successful, but I'm interested in trying ExpressMapper. http://www.expressmapper.org/ The examples to configure are very sparse.
I see from Github page it appears to be .netcore ready.
https://github.com/fluentsprings/ExpressMapper
How can I configure ExpressMapper in startup.cs so that I can DI into a controller, similar to Automapper?
If someone had a quick startup.cs example for ExpressMapper and .NET Core that would be great.
(And as an aside, if someone was using another competitive mapper tool with .NET Core that they would recommend, that would also be helpful).
With Automapper I can add a MappingProfile class then do something like
Startup.cs
private MapperConfiguration _mapperConfiguration { get; set; }
_mapperConfiguration = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new MappingProfile());
});
ConfigureServices
services.AddSingleton<IMapper>(sp => _mapperConfiguration.CreateMapper());
Controller
public ValuesController(IMapper mapper)
{
_mapper = mapper;
}
The same with Expressmapper you can use IMappingServiceProvider interface and MappingServiceProvider implementation that Mapper static class uses. Please take a look at the following example and here is the link to see it in real:
public class Program
{
public static void Main()
{
var container = new Container();
container.Register<IMappingServiceProvider, MappingServiceProvider>();
var mapper = container.GetInstance<IMappingServiceProvider>();
var result = mapper.Map<Test, TestResponse>(new Test{Name = "Just a test"});
Console.WriteLine("result is {0} type", result as TestResponse);
Console.WriteLine("result.Name = {0}", result.Name);
}
public class Test{
public string Name {get;set;}
}
public class TestResponse{
public string Name {get;set;}
}
}

Resources