Pass a value globally - c#

Im new to xamarin/c#, im trying to make an application with login , and Im trying to pass the logged in userid inside the application, the question is , how do I pass or make the user id keeps floating inside after the login page? Should I keep passing it in every page using queryproperty or there's better way to keep it , like a specific file to to put it so that every page can call it?

You can use the Application.Properties collection to store things that need to be accessible to the entire application.
To store the user ID you would use
Application.Current.Properties("UserID") = UserID;
and to retrieve it you would use
UserID = Application.Current.Properties("UserID");

In C# it's not possible to define true global variables (meaning that they don't belong to any class). using a static class is a valid alternative option so you can create something like this:
public static class Globals
{
public Dictionary<int, UserObject> Users = new Dictionary<int, UserObject>();
}
Now, you'll be able to access The Users's dictionary property and add/remove/modify login users
Following Hans Kesting comment, Please note that An Xamarin app servers a single user at at time, so you can refactor the above from a dictionary to UserObject

Static classes - bad practic for contains info. You can use IoC-container. I don't know xamarin, if you have startup-class (how WPF), you can make ioc-container:
Install Autofac Install-Package Autofac -Version 5.0.0
Rigster user content:
class StartupClass
{
public static IContainer Container { get; set; }
public void OnStartup()
{
var containerBuilder = new ContainerBuilder();
var userContent = smthMethodForGiveUserContent();
containerBuilder.RegisterInstance<User>(userContent); //register UserType!
Container = containerBuilder.Build();
}
}
Resolve user content:
class SmthClass
{
public void Method()
{
using(var scope = StartupClass.Container.BeginLifetimeScope())
{
var userContent = scope.Resolve<User>(); //IT'S YOUR OBJECT, USE!!
//smth code..
}
}
}

The quickest way is to define a variable in App, it can be accessible to the entire project .
Because App itself has been defined inside Application class ,and it is a static property .
public partial class App : Xamarin.Forms.Application
{
public string UserID;
}
// set or get
(App.Current as App).UserID

Related

How to declare global variable in Program cs and use it in controllers in .NET 6.0 Web Api

I have default Program.cs file from Web Api template in .NET 6.0.
I am adding variable "test" so I can use its value in controllers.
var builder = WebApplication.CreateBuilder(args);
const string test = "test123";
builder.Configuration.Bind(test);
//rest of the file...
And now I want to use variable "test" outside Program.cs but I have no idea how to do it. I cannot just simply use it because when trying to read it in controller like this:
string localVar = test;
I am getting an error "'test' is not null here. Cannot use local variable or local function declared in a top-level statement in this context".
This is probably some stupid mistake but I can't figure it out...
Starting C# 9, we don't need to explicitly mention the Main method in Program.cs file as we can use the top-level statements feature. However, it doesn't mean that we shouldn't use the default Program class in the created file at all. In your case, you have a need to define the static/const property so you can change the newly created structure into the old one.
namespace WebApplication;
public class Program
{
public static string Test { get; private set; }
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
Program.Test = "approach1";
builder.Services.Configure<MyOptions>(x => x.Test = "approach2");
///
}
public class MyOptions
{
public string Test { get; set; }
}
I assumed that you have a need to set the value to the Program.Test field during runtime, so in the first approach, I used the static field with a private set; accessor instead of the constant.
In the second approach, I used the C# options feature to configure the MyOptions.Test field value, this will be very flexible and useful to write unit tests later. But, you need to inject the MyOptions class wherever is required.
In the below controller template, I specified how to access the configured values at Program.cs file, inside the Get method
public class TestController : ControllerBase
{
private readonly MyOptions _myOptions;
public TestController (IOptions<MyOptions> myOptions)
{
_myOptions = myOptions.Value;
}
public IActionResult Get()
{
string test1 = Program.Test;
string test2 = _myOptions.Test;
///
}
}
Add public partial class Program { } at the very end of your Program.cs file and add constant, property or whatever you like in there.

how to properly use Scoped service from c# code

im using blazor server side and are therefore trying to use AddScoped instead of AddSingleton as the object is used on a per-user basis. I try to split the razor pages and the c# code as much as posible as i find this to be cleaner.
I add a scoped service using
Services.AddScoped<Services.ManageUserService>();
in the ConfigureServices function of the Startup class.
my problem now is to properly access the service from any .cs file (holding the logic of my .razor pages)
I have tried to do an injection like this:
[Inject]
public Services.ManageUserService manageUserService { get; set; }
and then accesed the scoped object using (username for example):
manageUserService.User
and this works. My problem is that if I add a print that is supose to only run once within the scoped service it runs every time the page is reloaded or changed.
for example, lets say i do this:
public class ManageUserService
{
public string User { get; private set; }
private bool LoadedStartup = false;
public ManageUserService() => SetupUser();
private void SetupUser()
{
if (!LoadedStartup)
{
User = "me";
LoadedStartup = true;
System.Diagnostics.Debug.Print("User:" + User);
}
}
}
I then access the class from multiply .cs files using:
[Inject]
public Services.ManageUserService manageUserService { get; set; }
The print "User:me" is supposed to only happen once as the locking bool LoadedStartup is changed, problem is that I get the print every time the Inject is happening (on change page, etc)
What am I doing wrong? aren't the AddScoped() suppose to add a "singelton" instance for every client? am I accessing it wrongly?
I can't find any examples of using AddScoped from separated .cs and .razor pages, only directly from the .razor page, and then it is done using #inject.
I was in the same situation:
1.- Add the scoped services:
Services.AddScoped<Services.ManageUserService>();
2.- Then, in order to have really the scoped instances once per user, in _Hosts.cshtml:
<app>
<component type="typeof(App)" render-mode="Server" />
</app>
3.- Now the trick I found by myself, instance the scoped services in App.razor
#inject Examples.ViewModels.MainViewModel Main;
#inject Examples.ViewModels.ChildViewModel Child;
#inject Examples.ViewModels.LayoutViewModel Layout;
#inject Examples.ViewModels.TreeViewModel Tree;
#{
Child.Main = Main;
Tree.LayoutViewModel = Layout;
}
4.- And if you have in the constructor something like:
public class MainViewModel
{
public static MainViewModel Instance;
public MainViewModel()
{
Instance = this;
}
}
You can access to any class you define as service from anywhere in your code.
MainViewModel.Instance...
I post about it at my blog: https://expediteapps.net/2020/02/18/scoped-viewmodels-instanced-once-on-start/

Xamarin Forms UWP Push Notification Click

I have implemented a push notification feature for my Xamarin.Forms UWP app and i am able receive notifications and then pop up a toast. I am using the code below for this.
//This method is triggered when a user taps/clicks the notification
private void Toast_Activated(ToastNotification sender, object args)
{
ToastActivatedEventArgs targs = (ToastActivatedEventArgs)args;
var xml = sender.Content.GetXml();
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xml);
XmlNodeList txts = xDoc.GetElementsByTagName("text");
var sitid = txts[0].InnerText;
var id = sitid.Substring(sitid.LastIndexOf(':') + 1);
Debug.WriteLine("Id:" + id);
}
When a user clicks/taps the notification, I want to open a specific page from my PCL project and pass this id variable as an argument. How can i do this ? Any help would be appreciated.
Use ServiceLocator or whatever other dependency injection framework you have to call your navigation service.
If you want to use the Xamarin Forms built in one see here - http://developer.xamarin.com/guides/cross-platform/xamarin-forms/dependency-service/
Basically you then define an interface of
public interface INavigationService
{
void NavigateTo(String pageKey);
}
Then you create a new class of
public class NavigationService: INavigationService
{
private NavigationPage _navPage;
public void Initialize(NavigationPage navPage)
{
_navPage = navPage;
}
public void NavigateTo(String pageKey)
{
// Get Page from pageKey
_navPage.PushAsync(page);
}
}
If you want to see how it is done in MVVMLight you can look here: http://www.mvvmlight.net/doc/nav1.cshtml
You can just use the ServiceLocator or other to get the navigation service as needed, whether its in native code or not.
OR
The other way around is that you Dependency Inject another Service type and load up another class from inside Forms. Then you just pass the action through to that, and it can perform the navigation while you are inside Forms.
I've use this tutorial for your purpose, I hope to help you

Setting custom username to Initializer Application Insights

I am looking to add a logged-in user's name to Application Insights to replace the cookie by default. I found this post that does something like this
public class RealUserIDTelemetryInitializer:ITelemetryInitializer
{
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
// Replace this with your custom logic
if (DateTime.Now.Ticks % 2 > 0)
{
telemetry.Context.User.Id = "Ron Weasley";
}
else
{
telemetry.Context.User.Id = "Hermione Granger";
}
}
}
My question, is how do I pass data into this? I have a login method on my Api that returns some info, but calling into this Initializer is something I am confused on.
We once had that same issue, although not for a user id but for an operation id. But the same principle can be applied here. We used a Func<T> that provides the data for the initializer (See full code of the initializerhere and the usage here).
public class RealUserIDTelemetryInitializer:ITelemetryInitializer
{
private readonly Func<string> usernameProvider;
public RealUserIDTelemetryInitializer(Func<string> usernameProvider)
{
this.usernameProvider = usernameProvider;
}
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
telemetry.Context.User.Id = usernameProvider.Invoke();
}
}
Then add the initializer using code:
configuration.TelemetryInitializers.Add(new RealUserIDTelemetryInitializer(() =>
CallContext.LogicalGetData(UserId)?.ToString()));
As you can see in our case the data was provided using data on the current thread but in your case I suppose there is easier access to the username so you wil probably inject another Func.

Unity - Inject an object to Constructor by calling a Method of a Class

I have the following interface and its implementation
public class DummyProxy : IDummyProxy
{
public string SessionID { get; set; }
public DummyProxy(string sessionId)
{
SessionId = sessionId;
}
}
public interface IDummyProxy
{
}
Then I have another class to get a session id
public class DummySession
{
public string GetSessionId()
{
Random random = new Random();
return random.Next(0, 100).ToString();
}
}
Now, in my Unity container, I would like to inject 'session id' to DummyProxy every time the container is trying to resolve IDummyProxy. But this 'session id' must be generated from DummySession class.
container.RegisterType<IDummyProxy, DummyProxy>(
new InjectionConstructor(new DummySession().GetSessionId()));
Is this even possible?
Your best approach for this would be to make use of an InjectionFactory, i.e.
container.RegisterType<IDummyProxy, DummyProxy>(new InjectionFactory(c =>
{
var session = c.Resolve<DummySession>() // Ideally this would be IDummySession
var sessionId = session.GetSessionId();
return new DummyProxy(sessionId);
}));
An InjectionFactory allows you to execute additional code when creating the instance.
The c is the IUnityContainer being used to perform the resolve, we use this to resolve the session and then obtain the session id, from that you can then create your DummyProxy instance.
You can do this. It's possible. It will only create the Session Id once (which is what I'd assume you'd want to do.)
I would personally rather give the DummyProxy a dependency on the DummySession (or better yet, an abstraction like IDummyProxy) and make it call DummySession.GetSessionID().

Categories