How to read appsettings.json in WPF Prism application - wpf

In a pure WPF application I can use HostBuilder, like below. But how could I do the same in a Prism app? What I read, HostBuild is not possible in Prism (according to Brian Lagunas it would not make sense - cannot find the link).
Could someone point me to the right direction or share a code?
public App()
{
_host = new HostBuilder()
.ConfigureAppConfiguration((context, configurationBuilder) =>
{
configurationBuilder.SetBasePath(context.HostingEnvironment.ContentRootPath);
configurationBuilder.AddJsonFile("appsettings.json", optional: false);
})
.ConfigureServices((context, services) =>
{
services.Configure<AppSettings>(context.Configuration);
services.AddDbContext<VisitorDbContext>(options =>
options.UseSqlServer(context.Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ISampleService, SampleService>();
services.AddScoped<IImportService, ImportService>();
services.AddSingleton<MainWindow>();
})
.ConfigureLogging(logging =>
{
//logging.AddConsole();
})
.Build();
}

If you use the .NET host to parse the app settings, you could override the RegisterTypes method of your Prism App.xaml.cs class to register the IOptions<T> with the app:
public partial class App : PrismApplication
{
private readonly IHost _host;
public App()
{
_host = ...;
}
...
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance(
_host.Services.GetService<IOptions<AppSettings>>());
}
}

Without using the host, this could be done like the following
public sealed partial class App
{
private readonly AppSettings? _appSettings;
public App()
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("appsettings.json", optional: false);
IConfigurationRoot configuration = configurationBuilder.Build();
IServiceCollection services = new ServiceCollection();
services.Configure<AppSettings>(configuration.GetSection(typeof(AppSettings).Name));
IServiceProvider provider = services.BuildServiceProvider();
IOptions<AppSettings>? options = provider.GetService<IOptions<AppSettings>>();
if (options is null)
throw new ApplicationException("App settings is not registered");
_appSettings = options.Value;
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance(_appSettings);
}
}
you would need to add the following package
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
and the following namespaces
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

Related

Navigation between Blazor Winforms pages

After a successfull experience with Blazor web server and Maui hybrid with Blazor, I'm now taking my first steps integrating my Blazor components in Winforms.
But I found no tutorials with samples of how to navigate between different pages/windows/forms.
Html links do not seem to work, and neither do NavigationManager:
Default page, app successfully loads it:
#inject NavigationManager NavigationManager
<div>
<button #onclick="OnClick">Go to page 2</button>
<a href="#" #onclick="#OnClick" #onclick:preventDefault>go to page 2</a>
<a href="page2" >go to page 2</a>
</div>
#code {
private void OnClick()
{
NavigationManager.NavigateTo("Page2");
}
}
Second page, no way to open it from previous one:
#page "/Page2"
<div>Wellcome to Page2</div>
Should I better navigate between Forms?
Update 1
I finally got it working having the component trigger an event on a form state class which instantiates a new form and shows it.
I guess I should better declare a singleton service with global appstate class in program.cs and hold all form instantiation from there, but I can't find the right way to declare it on a Blazor Winforms project.
My last version as follows, but appState is null when initializing Form1:
The singleton that communicates between Blazor compolnents and Winforms forms:
public class AppState
{
public event EventHandler<EventArgs>? GotoForm;
public void NavigateNext()
{
GotoForm?.Invoke(this,new EventArgs());
}
}
The component which feeds content to each form:
#inject AppState appState
<h3>Welcome to Form #FormNumber</h3>
<button #onclick="appState.NavigateNext">Goto page #AltFormNumber()</button>
#code {
[Parameter] public int FormNumber { get; set; }
int? AltFormNumber()=>FormNumber == null ? null : 3-FormNumber;
}
The default form:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
public partial class Form1 : Form
{
[Inject] AppState appState { get; }
public Form1()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
//services.AddSingleton<FormState>();
blazorWebView1.HostPage = "wwwroot\\index.html";
blazorWebView1.Services = services.BuildServiceProvider();
//var appState = blazorWebView1.Services.GetRequiredService<AppState>();
appState.GotoForm += NavigateNext;
blazorWebView1.RootComponents.Add<Pages.FormComponent>("#app",
new Dictionary<string, object?> { { "FormNumber", 1 } });
}
private void NavigateNext(object? sender, EventArgs args)
{
var frm = new Form2();
frm.Show();
}
}
The alternative form:
public partial class Form2 : Form
{
[Inject] AppState appState { get; }
public Form2()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
//services.AddSingleton<FormState>();
blazorWebView1.HostPage = "wwwroot\\index.html";
blazorWebView1.Services = services.BuildServiceProvider();
//var appState = blazorWebView1.Services.GetRequiredService<AppState>();
appState.GotoForm += NavigateNext;
blazorWebView1.RootComponents.Add<Pages.FormComponent>("#app",
new Dictionary<string, object?> { { "FormNumber", 2 } });
}
private void NavigateNext(object? sender, EventArgs args)
{
var frm = new Form1();
frm.Show();
}
}
The Program.cs:
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
var host = CreateHostBuilder().Build();
ServiceProvider = host.Services;
Application.Run(new Form1());
}
public static IServiceProvider ServiceProvider { get; private set; }
static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices((context, services) => {
services.AddSingleton<AppState>();
});
}
}

ABPFramwork - Remove api from layer application in swagger

I have created a project using abpframwork. When running swagger, swagger receives the function in the application layer is a api. I don't want that. Can you guys tell me how to remove it in swagger
Code in Application Layer
public class UserService : AdminSSOAppService, ITransientDependency, IValidationEnabled, IUserService
{
IUserRepository _userRepository;
private readonly ILogger<UserService> _log;
public UserService(IUserRepository userRepository,
ILogger<UserService> log
)
{
_userRepository = userRepository;
_log = log;
}
public async Task<List<UserDto>> GetList()
{
var list = await _userRepository.GetListAsync();
return ObjectMapper.Map<List<User>, List<UserDto>>(list);
}
public async Task<UserDto> GetUserById(int Id)
{
var user = await _userRepository.GetAsync(c=>c.Id == Id);
return ObjectMapper.Map<User, UserDto>(user);
}
}
Code in HttpApi Layer
[Area(AdminSSORemoteServiceConsts.ModuleName)]
[RemoteService(Name = AdminSSORemoteServiceConsts.RemoteServiceName)]
[Route("api/user/user-profile")]
public class UserController : ControllerBase, IUserService
{
private readonly IUserService _userAppService;
public UserController(IUserService userAppService)
{
_userAppService = userAppService;
}
[HttpGet]
[Route("get-list-httpapi")]
public Task<List<UserDto>> GetList()
{
return _userAppService.GetList();
}
[HttpGet]
[Route("get-by-id-httpapi")]
public Task<UserDto> GetUserById(int Id)
{
return _userAppService.GetUserById(Id);
}
}
I can suggest a workaround as to enable only the APIs you need to appear on swagger (though the ones that don't appear anymore will still be available for consumption).
I would suggest you add a configuration part in your *.Http.Api project module inside your ConfigureSwaggerServices, like so:
context.Services.AddSwaggerGen(options =>
{
options.DocInclusionPredicate(
(_, apiDesc) =>
apiDesc
.CustomAttributes()
.OfType<IncludeInSwaggerDocAttribute>()
.Any());
});
And for the attribute, it would be very simple, like so:
[AttributeUsage(AttributeTargets.Class)]
public class IncludeInSwaggerDocAttribute : Attribute
{
}
This will let you achieve what you want, however I still recommend reading the doc carefully to be able to implement DDD.

Unity: Type registered with ContainerControlledLifetimeManager is not being Singleton

I have an issue with a Unity Container in a WPF application. I am using Prism with UnityBootstrapper. I need to register a class as a singleton. This is the class:
public class RepositoryBase<T> : IRepository<T> where T : class, new()
{
private string connectionString;
public RepositoryBase(string conne)
{
connectionString = conne;
}
public async Task<List<T>> Get()
{
var db = GetDbConnection();
var value = await db.GetAllWithChildrenAsync<T>(recursive: true);
CloseDatabaseConnection(db);
return value;
}
}
In the bootstrapper, I register the above type as follows:
protected override void ConfigureContainer()
{
var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
var path = System.IO.Path.GetDirectoryName(assemblyLocation);
path = Path.Combine(path, "Data.db3");
Container.RegisterType(typeof(IRepository<>), typeof(Data.RepositoryBase<>), new ContainerControlledLifetimeManager(), new InjectionConstructor(path));
base.ConfigureContainer();
}
However, if I place a breakpoint in the constructor of the RepositoryBase class, I can see it breaks several times.
Can anyone see what am I doing wrong?

How to display new modal form in ReactiveUI 6.5

I am one of a team of developers currently maintaining a large suite of applications written using the WinForms UI.
In order to improve testability of our applications, we are wanting to move to an MVVM style, to separate the UI from the business logic. However, we need to keep using the WinForms UI, to minimize impact on our users as they work with different applications in the suite.
In trialing ReactiveUI, I have got a handle on how to bind form controls and commands to my view model, but cannot find documentation or examples on how to pop up a modal form to ask for or display additional information. For example these documentation pages on routing mention every supported UI framework except WinForms: http://docs.reactiveui.net/en/user-guide/routing/index.html, https://github.com/reactiveui/ReactiveUI/blob/docs/docs/basics/routing.md
Unfortunately, the ReactiveUI "good examples page" does not appear to have any WinForms-based examples, and all the other ReactiveUI / WinForms examples I can find using Google are only a single form.
I definitely want to keep forms/views out of the view model to maintain testability.
I believe the right way is to have a ReactiveCommand that is triggered by some user action in the view (such as clicking a button, selecting a menu item), but:
What should the command do?
Should it use Routing even though WinForms is not mentioned in the documentation? If yes, how is Routing done in a WinForms application?
How would the command/routing request the new form gets shown modally?
For simple messages and yes/no answers, I would look at Wayne Maurer's example for using UserError. I've used his example in Winform projects.
For something more complex, I was having the same difficulties finding any Winforms examples for routing. My google searches finally landed me in the source code for ReactiveUI.Winforms, where I discovered that Paul already has a UserControl for Winforms that will host routed UserControl views. It's called RoutedControlHost.
Using that code, I hacked something together that will show modal forms. I'm sure this isn't the best way to do it, but it might give you ideas.
RoutedModalHost
using Microsoft.Win32.SafeHandles;
using ReactiveUI;
using System;
using System.ComponentModel;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ReactiveUI_Test_Routing
{
public class RoutedModalHost : ReactiveObject, IDisposable
{
readonly CompositeDisposable disposables = new CompositeDisposable();
RoutingState _Router;
IObservable<string> viewContractObservable;
public RoutedModalHost()
{
this.ViewContractObservable = Observable.Return(default(string));
var vmAndContract =
this.WhenAnyObservable(x => x.Router.CurrentViewModel)
.CombineLatest(this.WhenAnyObservable(x => x.ViewContractObservable),
(vm, contract) => new { ViewModel = vm, Contract = contract });
Form viewLastAdded = null;
this.disposables.Add(vmAndContract.Subscribe(x => {
if (viewLastAdded != null)
{
viewLastAdded.Dispose();
}
if (x.ViewModel == null)
{
return;
}
IViewLocator viewLocator = this.ViewLocator ?? ReactiveUI.ViewLocator.Current;
IViewFor view = viewLocator.ResolveView(x.ViewModel, x.Contract);
view.ViewModel = x.ViewModel;
viewLastAdded = (Form)view;
viewLastAdded.ShowDialog();
}, RxApp.DefaultExceptionHandler.OnNext));
}
[Category("ReactiveUI")]
[Description("The router.")]
public RoutingState Router
{
get { return this._Router; }
set { this.RaiseAndSetIfChanged(ref this._Router, value); }
}
[Browsable(false)]
public IObservable<string> ViewContractObservable
{
get { return this.viewContractObservable; }
set { this.RaiseAndSetIfChanged(ref this.viewContractObservable, value); }
}
[Browsable(false)]
public IViewLocator ViewLocator { get; set; }
bool disposed = false;
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
// Free any other managed objects here.
//
this.disposables.Dispose();
}
// Free any unmanaged objects here.
//
disposed = true;
}
}
}
MainViewModel
using ReactiveUI;
using System.Reactive.Linq;
using System;
namespace ReactiveUI_Test_Routing
{
public class MainViewModel : ReactiveObject, IScreen
{
public RoutingState Router { get; private set; }
public ReactiveCommand<object> ShowTestModalForm { get; protected set; }
public MainViewModel(RoutingState modalRouter)
{
Router = modalRouter;
ShowTestModalForm = ReactiveCommand.Create();
ShowTestModalForm.Subscribe(x => Router.Navigate.Execute(new TestModalFormViewModel(this)));
}
}
}
MainView
using System.Windows.Forms;
using Splat;
using ReactiveUI;
namespace ReactiveUI_Test_Routing
{
public partial class MainView : Form, IViewFor<MainViewModel>
{
public MainView()
{
InitializeComponent();
IMutableDependencyResolver dependencyResolver = Locator.CurrentMutable;
dependencyResolver.Register(() => new TestModalFormView(), typeof(IViewFor<TestModalFormViewModel>));
RoutingState router = new RoutingState();
RoutedModalHost modalHost = new RoutedModalHost();
modalHost.Router = router;
this.BindCommand(ViewModel, vm => vm.ShowTestModalForm, v => v.ShowTestModalForm);
ViewModel = new MainViewModel(router);
}
public MainViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (MainViewModel)value; }
}
}
}
TestModalFormViewModel
using ReactiveUI;
namespace ReactiveUI_Test_Routing
{
public class TestModalFormViewModel : ReactiveObject, IRoutableViewModel
{
public IScreen HostScreen { get; protected set; }
public string UrlPathSegment { get { return "ModalForm"; } }
public TestModalFormViewModel(IScreen screen)
{
HostScreen = screen;
}
}
}

Translating Structure Map into Autofac

I have recently puchased a very good book on MVVM - MVVM Survival Guide For Enterprise Architectures in Silverlight and WPF
Unfortunatly, one of the sections relating to IoC has a lot of code samples for StructureMap which is not available for Silverlight
Can anyone point me to a link that would help me translate Structure Map code to Autofac which is the injection tool I am looking at using
The code uses the factory mehod of creating classes and a bootstrapper
using Northwind.ViewModel;
using StructureMap;
namespace Northwind.UI.WPF
{
public class BootStrapper
{
public MainWindowViewModel MainWindowViewModel
{
get
{
return ObjectFactory
.GetInstance<MainWindowViewModel>();
}
}
public BootStrapper()
{
ObjectFactory.Initialize(
o => o.Scan(
a =>
{
a.WithDefaultConventions();
a.AssembliesFromApplicationBaseDirectory(
d => d.FullName
.StartsWith("Northwind"));
a.LookForRegistries();
}));
}
}
using StructureMap;
namespace Northwind.ViewModel
{
public class CustomerDetailsViewModelFactory
: ICustomerDetailsViewModelFactory
{
private readonly IContainer _container;
public CustomerDetailsViewModelFactory(
IContainer container)
{
_container = container;
}
public CustomerDetailsViewModel CreateInstance(
string customerID)
{
return _container
.With("customerID")
.EqualTo(customerID)
.GetInstance<CustomerDetailsViewModel>();
}
}
}
Paul
Autofac and StructureMap work differently, so you can't "translate" it one to one.
However, this is what it should look like to accomplish the same.
I've made some assumptions as not everything is there to test out your code.
public class BootStrapper
{
private readonly ILifetimeScope _container;
public BootStrapper()
{
var builder = new ContainerBuilder();
Assembly[] assemblies =
GetAssembliesFromApplicationBaseDirectory(
x => x.FullName.StartsWith("Northwind"));
builder.RegisterAssemblyTypes(assemblies)
.AsImplementedInterfaces();
// Module in Autofac = Registry in StructureMap
builder.RegisterAssemblyModules(assemblies);
Assembly viewModelAssembly =
typeof(MainWindowViewModel).Assembly;
builder.RegisterAssemblyTypes(viewModelAssembly);
_container = builder.Build();
}
private static Assembly[] GetAssembliesFromApplicationBaseDirectory(Func<AssemblyName, bool> condition)
{
string baseDirectoryPath =
AppDomain.CurrentDomain.BaseDirectory;
Func<string, bool> isAssembly =
file => string.Equals(
Path.GetExtension(file), ".dll", StringComparison.OrdinalIgnoreCase);
return Directory.GetFiles(baseDirectoryPath)
.Where(isAssembly)
.Where(f => condition(new AssemblyName(f)))
.Select(Assembly.LoadFrom)
.ToArray();
}
public MainWindowViewModel MainWindowViewModel
{
get
{
return _container.Resolve<MainWindowViewModel>();
}
}
}
public class CustomerDetailsViewModelFactory : ICustomerDetailsViewModelFactory
{
private readonly ILifetimeScope _container;
public CustomerDetailsViewModelFactory(ILifetimeScope container)
{
_container = container;
}
public CustomerDetailsViewModel CreateInstance(string customerID)
{
return _container.Resolve<CustomerDetailsViewModel>(
new NamedParameter("customerID", customerID));
}
}

Resources