My intent is to create a payment processing service I can bolt onto the side of any website. My issue is injecting settings, such as a Stripe Public Key, a Paypal Merchant Id, and the specific url on the server to send the token and to charge stripe.
This is simplified for this question (and the Payments projects will end up being dll's), but the project structure looks like this:
The WebApp is the ASP.NET Core hosted Blazor WASM project template. WebApp.Client has dependencies on Payments.Shared and Payments.Ui while WebApp.Server has a dependency on Payments.Backend as well (for Stripe charge response etc models).
Since I want to use this Payments service on any site, and since these will be different for every site, I'll need to set the props in PaymentSettings.cs at startup.
Is there a way to do this? I only plan on using with Blazor WASM websites, if that matters.
I feel like I might be able to pass these settings down to the WebApp.Client through string[] args in Program.cs's Main method:
public static async Task Main(string[] args)
{
//omitted for brevity
//relevant line here
builder.Services
.AddSingleton(sp => new Payments.Shared.Infrastructure.PaymentSettings(args));
await builder.Build().RunAsync();
}
and then from there into the constructor for PaymentSettings.cs:
public class PaymentSettings
{
public PaymentSettings(string[] args)
{
//set props from args
}
public string PaypalMerchantId { get; set; }
public string StripeKey { get; set; }
public string ChargeStripeUrl { get; set; }
}
But I don't see any way to do this from the WebApp.Server project.
Maybe I'm overthinking this, I mean, if I can at least inject the base url of WebApp.Server into the Payments.Shared project, I can then run a get request for the rest of it.
Any help much appreciated.
I was overthinking it. Since it's all public info, I just hard-coded the info into Program.cs in the WebApp.Client:
public static async Task Main(string[] args)
{
//omitted for brevity
string stripeKey = "Hello dolly";
string paypalId = "How ye be";
string chargeStripeUrl = "https://all-about-dolly.com/chargeIt";
//relevant line here
builder.Services.AddSingleton(sp => new PaymentSettings
{
StripeKey = stripeKey,
PaypalMerchantId = paypalId,
ChargeStripeUrl = chargeStripeUrl
});
await builder.Build().RunAsync();
}
Might not be best practices, but it works for me.
Related
First of all excuse me for my bad english ...
I'm looking for a way to use a class in all my project. I explain my need to you. Because I don't know where to go.
I'have a web project asp net core connected to sql server. Inside i have a public pages with different section (ex: payment, create, message, ads, register...), and i have a area admin (back office for me).
I have a class :
public class Section
{
public bool RegistrationEnabled { get; set; }
public bool PaymentEnabled { get; set; }
public bool PublicationEnabled { get; set; }
public bool MessageEnabled { get; set; }
}
I want this class to allow me to make certain part of the site accessible or not in my views or in my controllers.
Ex in view :
#if (Section.RegistrationEnabled)
{
// Display form register..
}
The data is saved in a table in sql server (with entity framework), and I must be able to modify it from my admin area. I would like it to be persistent without having to fetch it each time a page is called by a user.
I have explored different solution:
appSetting: but I can't edit from my admin interface I think?
Resource file: But I don't think I can edit it directly?
Singleton?
Azure ?
I'm out of ideas and I don't know where to go ...
It's a bit of a broad/general question, but not a difficult problem to solve.
You can create a table in your database, e.g. AppSettings, with a Name and a Value string column. Probably you'd want to do this with your normal EF Core code first approach, creating the models, then doing a migration.
a) Then, you'll want to create a component that will automatically read these values from the database and insert them into the app configuration. For example this seems to be a good implementation candidate.
But, this begs the question when/how do you want this to be updated, since you mention that you don't want it to be read from the database in each request. Still, if you want to make it dynamically configurable, you will need some sort of re-reading. Unless you're comfortable restarting your service each time you modify the configuration.
b) Another option, instead of the configuration injector above, is to create a custom service, e.g. AppSettingsService (or preferably a less general name), that you can register as a singleton scoped service in the container, and inject into your consuming classes. This service would read the values from the DB, cache them in memory, and expose them in an easy to consume way; plus it could expose a method for refreshing the values from the DB.
If you need help with the implementation details of either of the above, let me know and I can add more details and code samples by editing this answer. Without knowing a bit more of your preferences it would be too demanding to cover everything in detail.
You can use dynamic configuration in asp.net core to accomplish this, I think it is the best way and I recommend you to use this approach. you can find a step by step guide here.
Else you can get your set of values from the database and store them in the session when the application is started. [Source]
First install nuget package - Microsoft.AspNetCore.Session
In Startup.cs file
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
// Add these 6 lines
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
.
.
.
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession(); //< --- And add this line
..............
}
And in C# you can set and retrive values from Session Storage
using Microsoft.AspNetCore.Http;
public class SomeController : Controller
{
public ActionResult Index()
{
HttpContext.Session.SetString("RegistrationEnabled", true ); // Set Session
var registrationEnabled = HttpContext.Session.GetString("RegistrationEnabled"); // Get Value of Session
}
}
Thanks, from your answers I tried to produce something that works, but I don't know if it's very relevant.
This is my setting class :
[Table("SETTING")]
public class Setting : BaseObject
{
[Key]
[StringLength(50)]
public string Key { get; set; }
[StringLength(50)]
public string Value { get; set; }
public e_SettingType Type { get; set; }
}
public enum e_SettingType
{
UNDEFINED,
BOOL,
BYTE,
CHAR,
DECIMAL,
DOUBLE,
FLOAT,
INT,
STRING,
}
And my service :
public class SettingService
{
private const string CACHE_KEY = "CurrentSetting";
private readonly IMemoryCache _cache;
private readonly IBaseRepository<Setting> _setting;
public SettingService(IMemoryCache cache, IBaseRepository<Setting> setting)
{
_cache = cache;
_setting = setting;
}
public async Task<bool> GetBool(string key)
{
var settings = await GetFromCache();
if (settings != null && settings.Any(x => x.Key.Equals(key)))
return bool.Parse(settings.First(x => x.Key.Equals(key)).Value);
else
return default;
}
public async Task<string> GetString(string key)
{
var settings = await GetFromCache();
if (settings != null && settings.Any(x => x.Key.Equals(key)))
return settings.First(x => x.Key.Equals(key)).Value;
else
return default;
}
private async Task<List<Setting>> GetFromCache()
{
if (!_cache.TryGetValue(CACHE_KEY, out List<Setting> data))
{
var models = await _setting.FindAll();
data = models.ToList();
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(30));
_cache.Set(CACHE_KEY, data, cacheOptions);
return data;
}
else
return data;
}
}
I've add this in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<SettingService>();
}
And for consume my setting in my view, i use this :
#inject MyProject.Service.SettingService setting //top of view
Get in my view :
#setting.GetString("MY_KEY").Result
But is this relevant? Or good practice?
I'm just taking my first baby steps in the MEF territory and wanted to do so using .net core 2.1.
Using VS 2017 (version 15.8.8) I've done a small Console App (.NET Core) with an interface
interface IMessageSender
{
void Send(string message);
}
and an implementation (in the same project)
[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine("EmailSender : " + message);
}
}
Finally I have a small compose method executed from my Main(string[] args)
[Import]
private void Compose()
{
var assembly_A = new[] { typeof(Program).GetTypeInfo().Assembly };
var config_A = new ContainerConfiguration().WithAssembly(assembly_A[0]);
var container_A = config_A.CreateContainer();
var msg_A = container_A.GetExport<IMessageSender>();
msg_A.Send("Hello");
}
It works as expected
However, if I add a new class library to my solution and move my implementation of Send(string) to the newly added project things do not work out.
namespace AnotherMefExtensionProjectNamespace
{
[Export(typeof(IMessageSender))]
public class EmailSenderExtended : IMessageSender
{
public void Send(string message)
{
Console.WriteLine("EmailSenderExtended : " + message);
}
}
}
The new Compose method
[Import]
public IMessageSender MessageSender { get; set; }
private void Compose()
{
var assembly_B = new[] { typeof(EmailSenderExtended).GetTypeInfo().Assembly };
var config_B = new ContainerConfiguration().WithAssembly(assembly_B[0]);
var container_B = config_B.CreateContainer();
var msg_B = container_B.GetExport<IMessageSender>();
msg_B.Send("Hello");
}
I've tried to compare the different configs and containers (_A versus _B in the examples) but can't understand what is different. I've even tried to extend the class ContainerConfiguration to load from a specified assembly and it works as long as the given file contains the Main method but fails if I use my "extended" .NET Core Class Library.
public static ContainerConfiguration WithChosenAssembly(this ContainerConfiguration configuration, string pathAndFile)
{
var context = AssemblyLoadContext.Default.LoadFromAssemblyPath(pathAndFile);
var ass_list = new List<Assembly>() { context };
configuration = configuration.WithAssemblies(ass_list, null);
return configuration;
}
I was under the impression that you extend your main application by developing a class library that basically implements the interfaces specified.
I seem to be unable to do this currently, but obviously I misunderstood something very basic.
If someone would care to put me on the right track or give me an alternative idea for "plug-in" development for .net core I would be very grateful.
King regards
Magnus
I realized that my test setup does not mimic any real world scenario and thus I brought my problems on myself.
Obviously I should have had three projects.
One project with only the interface definitions.
One "main" project where all my regular code exists.
One (or more) projects where my MEF implementations of the interfaces exist.
Reviewing my example and adhering to the obvious "design" above it all works exactly as it should.
Most StackOverflow users probably wouldn't make my blunder but for those that did, I hope the above helps. :-)
I came here reaching for your help. I am fairly new when it comes to SignalR and after several days of reading and retrying stuff again and again I have reached a dead end. Straight to the point.
I have a solution which includes several projects in it. The ones that are giving me trouble are the Web and a class library.
I have SignalR installed in the Web project and the SignalR Client in the Class Library.
My target is to track and monitor the progress of Long running processes that are running in the Class Library. I have SignalR working properly from the Web.
This is my Hub method that I am trying to call:
public void SendProgress(string progressMessage, int progressCount, int totalItems)
{
var percentage = (progressCount * 100) / totalItems;
Clients.All.sendProgress(progressMessage, percentage + "%");
}
Moreover I have a method in the Class Library (ImportProcess) which contains a foreach loop. Inside that foreach loop I am calling:
public void SendProgress(string message, int currentItem, int totalItems)
The body of the SendProgress is this:
public void SendProgress(string message, int currentItem, int totalItems)
{
var hubConnection = new HubConnection("http://localhost:13310/");
IHubProxy statementsHubProxy = hubConnection.CreateHubProxy("statementsHub");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
hubConnection.Start().Wait();
statementsHubProxy.Invoke("SendProgress", message, currentItem, totalItems);
}
The problem is that since I am calling the SendProgress inside a foreach, I consider it wrong to define the connection everytime that SendProgress is called.
I tried including the Connection properties to the constructor of the class. It worked but it only returns the first iteration of the foreach and ignored all the others.
I have to mention though, that if I have the connection properties inside the SendProgress method, the code works and I am receiving the information I want in the cosnole, but again, I think it shouldn't be like that.
This is the MVC controller which calls a rest service which in turn calls the class library
public JsonResult AProcess()
{
_dataImportService.DataImportProcess("2018-02-27", "2018-02-28", "This is another parameter");
return Json("", JsonRequestBehavior.AllowGet);
}
Please ignore the parameters of the DataImportProcess as they are irrelevant at this point.
This is my Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
I am using SignalR 2.2.2
Edit #1
I included the connection properties to the constructor of the class again like this:
static IHubProxy _proxy { get; set; }
public ImportClass()
{
var hubConnection = new HubConnection("http://localhost:13310/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
IHubProxy statementsHubProxy = hubConnection.CreateHubProxy("statementsHub");
_proxy = statementsHubProxy;
hubConnection.Start().Wait();
}
But no luck. SendProgress method is not sending any updates at all.
Any help is much, much appreciated.
Best regards,
Konstantinos.
I've read a dozen questions here on SO and at least 4 blog posts but I cannot get this to work in a VS2017 solution with multiple class library projects. However, I can get it to work with a solution with a web application and one class library, albeit with a different pattern than the one I've found in the documentation and here on SO.
My solution contains 5 projects a WebApplication, Testing, Infrastructure, Services, Domain (the latter three are class libraries).
In the infrastructure project I have created a class called EmailConfigurationSettings and registered it in the StartUp class of the WebApp.
This class is in class library:
public class EmailConfigurationSettings
{
public string Provider { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
This is in StartUp.cs in WebApp:
services.Configure<EmailConfigurationSettings>(Configuration.GetSection("EmailConfigurationSettings"));
services.AddOptions();
appsettings.json
"EmailConfigurationSettings": {
"Provider": "gmail",
"Username": "user#gmail.com",
"Password": "mypassword"
}
I need the settings in the Services project. But for now I am just trying to ensure I can actually retrieve them. The code below follows all the code samples I found but generates:
public class LoadEmailSettingsFromAppSettings
{
private readonly IOptions<EmailConfigurationSettings> _emailSettings;
public LoadEmailSettingsFromAppSettings(IOptions<EmailConfigurationSettings> emailSettings)
{
_emailSettings = emailSettings;
Message = "Our provider is " + _emailSettings.Value.Provider;
}
public string Message { get; set; }
}
_emailSettings is always null
So I tried testing it in the TestProject:
IOptions<EmailConfigurationSettings> someOptions = Options.Create<EmailConfigurationSettings>(new EmailConfigurationSettings());
Well, that's null too.
In the other solution, the one with the WebApp and one class library,
using a slightly different pattern I found on one of the blogs I can actually retrieve the values from appsettings.json:
public class AuthMessageSender : IEmailSender, ISmsSender
{
public AuthMessageSender(IOptions<EmailConfig> emailSettings)
{
_emailSettings = emailSettings.Value;
}
public EmailConfig _emailSettings { get; }
public Task SendEmailAsync(string email, string subject, string message)
{
Execute(email, subject, message).Wait();
return Task.FromResult(0);
}
Please note the IOptions class was different with 9 properties but constructed the same way as the pattern above.
But using this pattern does not work in the multiple class library project. How can I understand how to get this to work in a multiple class library scenario?
Why they abandoned the ConfigurationManager I don't know, it was far easier.
In microservices you can simple do a bind to a new object that matches the json definition. So you would do.
var emailConfig = new EmailConfigurationSettings();
Configuration.GetSection("EmailConfigurationSettings").Bind(emailConfig);
services.AddSingleton(emailConfig);
Once you do this, all you have to do is request a copy of EmailConfigurationSettings in your service layers constructor. This will give you the dependency injected singleton of that object.
I would set a breakpoint on the "bind" and make sure that email config is populated.
Here is an interesting answer about class library : Class library.
In this part of your code:
private readonly IOptions<EmailConfigurationSettings> _emailSettings;
public LoadEmailSettingsFromAppSettings(IOptions<EmailConfigurationSettings> emailSettings)
{
_emailSettings = emailSettings;
the dependency injection don't work because you are in a class library so you should keep it that way.
Somewhere in your webapp you construct LoadEmailSettingsFromAppSettings
public class SomeClass
{
public SomeClass(IOptions<EmailConfigurationSettings> emailSettings) // dependency injection works in your webapp
{
var loadEmail = new LoadEmailSettingsFromAppSettings(emailSettings); // send EmailSettings to your other class in your class library
//(...)
}
}
And the private readonly should be:
public IOptions<EmailConfigurationSettings> EmailSettings {get; private set;}
readonly vs get/set properties
I hope is clear and helpful
I'm working on a legacy ServiceStack application, and I'm trying to add a new endpoint. It's a servicestack 3 application. I created new Response, Request, and Service classes for it, like I've done countless times for other endpoints, but for some reason I can't seem to call it.
I even tried just copying previously existing files and rewriting them to fit my needs, but that didn't work either.
Here's my request:
[Route("/OSTicket/UpdateList", "POST")]
public class OSTicketUpdateListByDBIDRequest
{
public int DatabaseID { get; set; }
}
Here's my response:
public class OSTicketUpdateListResponse : IHasResponseStatus
{
public ResponseStatus ResponseStatus { get; set; }
}
And here's my service endpoint:
public OSTicketUpdateListResponse Post(OSTicketUpdateListByDBIDRequest request)
{
OSTicketUpdateListResponse response = new OSTicketUpdateListResponse();
response.ResponseStatus = new ResponseStatus();
response.ResponseStatus.ErrorCode = "200";
return response;
}
Do I need to register these files somewhere? I don't remember having to do that in the past, but it's very possible that I'm missing something. Whenever I POST to the application using DHC, I get a 404.
Figured it out. In my service declaration:
public class OSTicketService
I forgot to inherit Service.