Can you use IMemoryCache in a maui application - Getting null - xamarin.forms

Not sure what I am doing wrong but I would like to use caching in a Maui application but the values do not seem to be retained.
In the maui Program I have
builder.Services.AddSingleton<IMemoryCache, MemoryCache>()
using ServiceProvider? serviceProvider = builder.Services.BuildServiceProvider();
var cache= serviceProvider.GetService<IMemoryCache>();
cache?.Set("key", "someValue");
android mainActivity
var cache= MauiApplication.Current.Services.GetService(typeof(IMemoryCache)) as IMemoryCache;
var someValue=memoryCache.Get("key");
the cache object is there but there are no items in the cache ...someValue is null ?
what am I missing ? Why are the values not in the cache????
Update when I read the cache value that was set earlier on its null but things start working once in the viewmodel
private readonly IMemoryCache? cache;
public class MainActivity : MauiAppCompatActivity
{
private readonly IMemoryCache? cache;
public MainActivity()
{
var serviceProvider = MauiApplication.Current.Services;
cache = serviceProvider.GetService(typeof(IMemoryCache))as IMemoryCache ;
}
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
var someValue = cache.Get("key");
}
}

You could access and set the cache in App.xaml, like the following
public partial class App : Application
{
public App(IServiceProvider serviceProvider)
{
InitializeComponent();
MainPage = new AppShell();
var cache = serviceProvider.GetService(typeof(IMemoryCache)) as IMemoryCache;
cache?.Set("key2", "someValue2");
}
}
Hope it works for you.

Related

ASP.NET Registering Services with Ninject

I've taken the project I'm currently working on and I have to be deploying soon, however I'm having some issues with WCF. I have the RestService and the IRestService and when I make some test calls everything is working fine. However I want to use some of the data services I have created so I could query the database in order to perform CRUD operations. Here is what I have in the Service:
public readonly ITimesheetService timesheetService;
public readonly IProjectService projectService;
public readonly IUserService userService;
public readonly INotificationService notificationsService;
public readonly IDepartmentService departmentService;
public readonly IUserTokenService userTokenService;
public TimesheetRestService(ITimesheetService timesheetService, IProjectService projectService, IUserService userService, INotificationService notificationService, IDepartmentService departmentService, IUserTokenService userTokenService)
{
this.timesheetService = timesheetService;
this.projectService = projectService;
this.userService = userService;
this.notificationsService = notificationService;
this.departmentService = departmentService;
this.userTokenService = userTokenService;
}
public TimesheetRestService()
{
}
I had to add constructor without parameters otherwise it's not working. And then I have the following 2 methods:
public string[] NewMethod()
{
string[] data = new string[] { "grant_type", "p_username", "p_password" };
return data;
}
public IEnumerable<DepartmentServiceModel> GetDepertment()
{
string userId = GetUserId();
if (userId == null)
{
return null;
}
var deparments = this.departmentService.GetDepartments(userId).ToList().AsQueryable().To<DepartmentServiceModel>().ToList();
return deparments;
}
The NewMethod() is there just for testing purposes. My problem is with the GetDepartment() method as the departmentService and all the other services are null.
The developer before me has been using ninject, so I have tried adding the following code to the RegisterServices(IKernel kernel):
kernel.Bind(b => b.From(Assemblies.RestService).
SelectAllClasses().
BindDefaultInterface());
kernel.Bind<ITimesheetRestService>().To<TimesheetRestService>();
However when the TimeSheetRestService class is being initialized - the constructor with no parameters is being called. How can I call the constructor with the other services so I could use them to pull data from the database?

Unity crashes Web API, no log

I want to place my HTML parser into a singleton instance, so I utilized Unity to create and hold my class. Unfortunately this causes the app crash, and I have no information about the error.
I have Elmah but no entry in the datatable.
My bits are:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configuration.Filters.Add(new ElmahErrorAttribute());
UnityConfig.RegisterComponents();
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
Controller:
public class AccountsController : BaseApiController
{
private readonly ITemplateService _templateService;
public AccountsController() { }
public AccountsController(ITemplateService templateService)
{
_templateService = templateService;
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<ITemplateService, TemplateService>(new ContainerControlledLifetimeManager());
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
No log, no place to set up a breakpoint where the error is, app is crashing.
There is always a log in the EventViewer
Open it and there you find what is crash your program.

Xamarin Forms overriding OnAppearing causes app to crash

I am trying to do a simple override and load some data when my page loads, I am using the following code in the code behind page.
namespace XYZ
{
public partial class MainPage : ContentPage
{
private Label results;
private Label groupResults;
public MainPage()
{
InitializeComponent();
results = new Label();
groupResults = new Label();
}
protected override void OnAppearing()
{
base.OnAppearing();
storeIdTxt.Text = Settings.StoreIdSetting;
}
}
}
If I uncomment the override things works just fine, the error I am getting seems to be a generic one attached here
my settings class is fairly simple as follows
using System;
using System.Collections.Generic;
using System.Text;
using Plugin.Settings;
using Plugin.Settings.Abstractions;
namespace NWMPosNG.Helpers
{
/// <summary>
/// This is the Settings static class that can be used in your Core solution or in any
/// of your client applications. All settings are laid out the same exact way with getters
/// and setters.
/// </summary>
public static class Settings
{
private static ISettings AppSettings
{
get
{
return CrossSettings.Current;
}
}
#region Setting Constants
private const string SettingsKey = "settings_key";
private static readonly string SettingsDefault = string.Empty;
private const string StoreId = null;
private static readonly string StoreIdDefault = "0";
#endregion
public static string GeneralSettings
{
get
{
return AppSettings.GetValueOrDefault(SettingsKey, SettingsDefault);
}
set
{
AppSettings.AddOrUpdateValue(SettingsKey, value);
}
}
public static string StoreIdSetting
{
get
{
return AppSettings.GetValueOrDefault(StoreId, StoreIdDefault);
}
set
{
AppSettings.AddOrUpdateValue(StoreId, value);
}
}
}
}
I narrowed down the issue to when I access the saved data using
storeIdTxt.Text = Settings.StoreIdSetting;
But I don't understand why that causes the crash.
You are using the Settings Plugin from James Montemagno. Which is pretty much a KeyValuePair that is stored on the local device across sessions.
In your case:
AppSettings.GetValueOrDefault(StoreId, StoreIdDefault);
Translates to:
AppSettings.GetValueOrDefault(null, "0");
Which crashes because 'null' can't be a key. That's why setting the key (StoreId) prevents the crash from happening.
This line was the culprit
private const string StoreId = null;
I don't really understand why but setting this to a non NULL value makes the crash go away

Asp.net Identity DbContext / Repository Issue

I am using Asp.Net identity within my MVC app. I can see that this has it's own ApplicationDbContext - albeit it is connected to the same SQL db as my own DbContext I am using elsewhere.
So I am trying to access some of my own data via my own code within the AccountController - it does not seem to work I presume because of some confusion over which DBContext it thinks is active?
My Code :
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private PostageManager postmgr;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, PostageManager _postmgr)
{
UserManager = userManager;
SignInManager = signInManager;
postmgr = _postmgr;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//create select list items for countries drop down
List<SelectListItem> countries;
countries = postmgr.GetCountries().Select(item => new SelectListItem
{
Value = item.Country,
Text = item.Country
}).ToList();
countries.Insert(0, new SelectListItem { Value = string.Empty, Text = "Select delivery country or region...", Selected = true });
RegisterViewModel mode = new RegisterViewModel
{
Countries = countries
};
return View();
}
}
}
PostageManager is just a class that sits over my DAL to fetch some data (which uses repository pattern) - I'm using just a kind of pass through method to grab a list of countries, and using it in exactly the same way I have in other controllers which works fine. Underneath that class is my repository code that is linked to my default connection string (DBContext). It's balking at the following line with a null reference exception, I think postmgr is null :
countries = postmgr.GetCountries().Select(item => new SelectListItem
In reverse to get access to the identity data within my own controllers I have done the following :
public BasketController(BasketManager _mgr, PostageManager _postmgr, ProductManager _prodmgr)
{
mgr = _mgr;
postmgr = _postmgr;
prodmgr = _prodmgr;
shopper = Cart.GetShopperId();
this.applicationDbContext = new ApplicationDbContext();
this.userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.applicationDbContext));
}
protected ApplicationDbContext applicationDbContext { get; set; }
protected UserManager<ApplicationUser> userManager { get; set; }
Which as far as I understand it points the identity code to use the right DbContext - I looked at doing this in reverse in my AccountController but can't fathom it out.
I basically just want to be able to use my own code that grabs my own data from within the Identity controllers to help pass extra data etc through to the views.
I might be wrong but most probably postmgr field is not initialized from constructor and that is why you have this error.
Explanation:
By default Asp will try to create controller instance by constructor without parameters. If Asp can't find constructor without parameters it will try to call constructor with parameters, but to make it possible you have to configure IoC in your app. As your controler has constructor without parameters it will be selected by Asp. So all 3 fields are empty.
But in properties SignInManager and UserManager you try to take value from field or from OwinContext. As field is empty your code will take value from OwinContext. OwinContext is quite complex and smart tool that create its context automatically based on configuration provided in Startup.Auth.cs file or any other file under App_Start folder.
I think I have figured it out - added the following to my NinjectControllerFactory :
ninjectKernel.Bind<IAuthenticationManager>().ToMethod(c => HttpContext.Current.GetOwinContext().Authentication); //.InRequestScope();
ninjectKernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
ninjectKernel.Bind<UserManager<ApplicationUser>>().ToSelf();
ninjectKernel.Bind<IRoleStore<IdentityRole, string>>().To<RoleStore<IdentityRole, string, IdentityUserRole>>();
ninjectKernel.Bind<RoleManager<IdentityRole>>().ToSelf();
And changed my constructor to :
public AccountController(PostageManager _postmgr)
{
postmgr = _postmgr;
}

Why is the identity not loaded when resolving WebApi but is when resolving Mvc controllers

I am using Autofac for an Inversion of Control container which is configured like this
public void Configuration(IAppBuilder app) {
configureIoC(app);
configureAuth(app);
}
void configureIoC(IAppBuilder app) {
var b = new ContainerBuilder();
//...
b.Register(c => HttpContext.Current?.User?.Identity
?? new NullIdentity()).InstancePerLifetimeScope();
var container = b.Build();
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
I believe the fact that this is Autofac versus some other container is probably irrelevant to what I'm seing. They key line here is the one configuring any dependency on IIdentity to be plucked from HttpContext.Current.
I use it like this so that I can have stub-able access to the current user anywhere I want.
public interface ICurrentUser {
Task<AppUser> Get();
}
public class CurrentUserProvider : ICurrentUser {
public async Task<AppUser> Get() => await users.FindByNameAsync(currentLogin.GetUserId());
public CurrentUserProvider(AppUserManager users, IIdentity currentLogin) {
this.users = users;
this.currentLogin = currentLogin;
}
}
I've used this pattern on past projects and it works fine. I'm currently applying it to an existing project and seeing a very strange thing.
When an Asp.net Mvc controller depends on ICurrentUser everything works fine
When a WebApi controller gets an instance of ICurrentUser the Get operation fails since the instance of IIdentity has not been parsed from the cookie and does not yet have Claims loaded into it (AuthenticationType == null)! Oddly, if I pause the debugger after the WebApi controller is instantiated I can hit HttpContext.Current.User.Identity and see that AuthenticationType == "Cookie" and all claims are there.
What this leads me to conclude is that somehow things are happening in the following order
If this is a web api route, the Web Api controller creates an instance
Asp.Net Identity fills out the current HttpContext Identity
If this is an mvc route, the mvc controller creates an instance
Any actions are executed
This of course makes no sense at all!
So the questions are as follows
Is my inference of the order of things in the pipeline correct?
How can I control it to work properly? Why would this have worked on other projects but be causing problems here? Am I wiring something up in the wrong order?
Please don't suggest that I create an IdentityProvider to late-resolve IIdentity. I understand how I can fix the issue, what I don't understand is why this is happening to begin with and how to control the pipeline order of things.
I modified your code just a little, since I don't have NullIdentity() and your CurrentUserProvider wasn't compiling here.
I'm installed these packages:
Autofac
Autofac.Owin
Autofac.Owin
Autofac.Mvc5
Autofac.Mvc5.Owin
Autofac.WebApi2
Autofac.WebApi2.Owin
My Startup.cs looks like this:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
configureIoC(app);
ConfigureAuth(app);
}
void configureIoC(IAppBuilder app) {
var b = new ContainerBuilder();
//...
b.RegisterType<CurrentUserProvider>().As <ICurrentUser>().InstancePerLifetimeScope();
b.Register(c => HttpContext.Current.User.Identity).InstancePerLifetimeScope();
b.RegisterControllers(typeof(MvcApplication).Assembly);
b.RegisterApiControllers(typeof(MvcApplication).Assembly);
var x = new ApplicationDbContext();
b.Register<ApplicationDbContext>(c => x).InstancePerLifetimeScope();
b.Register<UserStore<ApplicationUser>>(c => new UserStore<ApplicationUser>(x)).AsImplementedInterfaces().InstancePerLifetimeScope();
b.RegisterType<ApplicationUserManager>().InstancePerLifetimeScope();
b.RegisterType<ApplicationSignInManager>().InstancePerLifetimeScope();
var container = b.Build();
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
Your ICurrentUser stuff:
public interface ICurrentUser
{
Task <ApplicationUser> Get();
}
public class CurrentUserProvider : ICurrentUser
{
private ApplicationUserManager users;
private IIdentity currentLogin;
public async Task<ApplicationUser> Get()
{
return await users.FindByNameAsync(currentLogin.GetUserId());
}
public CurrentUserProvider(ApplicationUserManager users, IIdentity currentLogin)
{
this.users = users;
this.currentLogin = currentLogin;
}
}
Therefore Global.asax:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
My HomeController which is quite simple:
public class HomeController : Controller
{
private ICurrentUser current;
public HomeController(ICurrentUser current)
{
this.current = current;
}
public ActionResult Index()
{
var user = current.Get();
if (user == null)
throw new Exception("user is null");
return View();
}
}
...and finally a simple ApiController, which I access by typing localhost/api/TestApi/5:
public class TestApiController : ApiController
{
private ICurrentUser current;
public TestApiController(ICurrentUser current)
{
this.current = current;
}
public string Get(int id)
{
var user = current.Get();
if (user == null)
throw new Exception("user is null");
return "";
}
}
If I just start the project (without even logging in), I receive a GenericIdentity object to support IIdentity interface, look at this:
And when I step in (F11) in the Get() method, the IIdentity is properly set with that GenericIdentity, because actually there is no one Logged in the application. That's why I think you don't actually need that NullableIdentity.
Try comparing your code with mine and fix yours so we can see if it works, then eventually you'll find out what was the real cause of the problem, rather than just fixing it (we developers like to know why something just got working).

Resources