We have a Xamarin.Forms app with FreshMvvm. Now, as Xamarin.Forms will not get support beginning next year, I am re-writing the app with .Net Maui. For MVVM pattern, I am trying to use CommunityToolkit.Mvvm. But I wonder how I can initialize the viewmodel now. With FreshMvvm I could override Init(), but CommunityToolkit.Mvvm does not seem to have anything like this. What is the right way to initialize the viewmodel asynchronously, as there is no async constructor?
In FreshMVVM, since the model has impelmented the FreshBasePageModel, you can override the Init() method and initialize data like the pseudo code below:
public override void Init (object initData)
{
//initialize data
}
However, in CommunityToolkit.Mvvm, you can set the data whatever you want in the default constructor like below:
public partial class MainPage : ContentPage
{
public MainPage(MainPageViewModel vm)
{
InitializeComponent();
BindingContext = vm;
Initialize();
}
public async void Initialize()
{
//await operation
}
}
Official reference link:https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/introduction
Related
I know that one way to use a context is via the using statement.
I use it like so within my controllers
[ApiController]
public class MyController : ControllerBase
{
[HttpPost]
public ActionResult PostActionHere(ActionRequestClass request)
{
using (var context = new MyEntityFrameworkContext())
{
....
// use context here
context.SaveChanges()
....
}
}
}
I would like to start injecting it into my controller. Mainly because I think it is easier to read and is more uniform with .NET Core dependency injection.
[ApiController]
public class MyController : ControllerBase
{
private MyEntityFrameworkContext _myDb;
public MyController(MyEntityFrameworkContext myDb)
{
_myDb = myDb;
}
[HttpPost]
public ActionResult PostActionHere(ActionRequestClass request)
{
....
// use context here
_myDb.SaveChanges()
....
}
}
Within my startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyEntityFrameworkContext >(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyEntityFrameworkDatabase")));
}
What I am worried about is that injecting it I lose the disposal properties that come with the using statement. Is that true? Feel free to suggest alternate approaches.
injecting it I lose the disposal properties that come with the using statement. Is that true?
No:
The AddDbContext extension method registers DbContext types with a
scoped lifetime by default.
Configuring a DbContext
And when the scope (here the HttpRequest) ends, the Scoped Lifetime object will be Disposed.
I am running into an issue where I can't figure out how to properly dispose of my object context I am creating every time I instantiate a new object.
public class OrderBLL{
var _iOrderLineDal;
public OrderBLL(){
_iOderLineDal = new OrderLineDal(new entityOBject(dbconnectionstring);
}
public OrderBLL(iOrderLineDal mockOrderLineDal){
_iOrderLineDal = mockOrderLineDal;
}
}
So the problem is, that every 30 seconds my service creates a new instance of the OrderBLL and then runs a method to see if there are any new orders in the Data base.
So every 30 seconds I create a new entityObject that is not being disposed of. the old implementation of the code was written using the using statement.
public bool HasNewOrders(){
using(var entityObject = new entityObject(dbconnectionString)){
var newOrders = entityObject.GetNewOrders();
}
//some logic
}
The problem with using this using statement is I cannot mock out the entityObject and easily write unit tests on any methods inside this OrderBLL class.
I tried disposing of it with a dispose method inside the OrderLineDal and once i got the data called dispose. That worked well the first iteration but the following iterations, the next 30 seconds, it would say that the entityObject was disposed of and cannot be used. (doesn't make sense to me, since I am creating a new one every time?)
Is there a way I can implement this repository pattern and still dispose of all the new entityObjects so I can mock the DAL out for unit testing?
I am working with EF 4. and it was not set up Code First, so I do not have POCO.
Ideally you would want to create your context outside of your OrderBLL (search google for Repository pattern).
public class OrderRepository : IOrderRepository, IDisposable
{
private readonly IOrderDBContext _dbContext;
// poor mans dependency injection
public OrderRepository() : this(new OrderDbContext("YourConnectionString")
{}
public OrderRepository(IOrderDBContext dbContext)
{
if (dbContext == null) throw new ArgumentNullException("dbContext");
_dbContext = dbContext;
}
public bool GetNewOrders(){
return _dbContext.Orders.Where(o => o.IsNew==true);
}
public void Dispose()
{
if (_dbContext != null) _dbContext.dispose();
}
}
public class OrderBLL : IOrderBLL
{
private readonly IOrderRepository _repository;
public OrderRepository(IOrderRepository repository)
{
if (repository == null) throw new ArgumentNullException("dbContext");
_repository = repository;
}
public bool HasNewOrders(){
var newOrders = _repository.GetNewOrders();
if (newOrders==null) return false;
return newOrders.Count() > 0;
}
}
[Test]
public void HasNewOrders_GivenNoNNewOrdersRetunedFromRepo_ReturnsFalse()
{
// test using nunit and nsubstitute
// Arrange
var repository = Substitue.For<IOrderRepository>();
var emptyOrderList = new List<Order>();
repository.GetNewOrders().Returns();
var orderBLL = new OrderBLL(repository);
// Act
var result = orderBLL.HasNewOrders();
// Assert
Assert.Equals(false, result);
}
Now you can inject your context into this class and easily test your business logic. Eventually you will need to create your dbContext and should also always expose this. I would suggest having a look at a DI container like Castle Windsor to manage the life of your objects, although in a service you may just want to manually create and dispose your context as close to the code entry point as possible (e.g. in the main method)
I am trying to implement Logout functionality in a Windows Store app that I am working on which is using Caliburn Micro.
The challenge that I am having is that if a user logs out, and then logs in again as another user, the ViewModels instantiated at app launch still exists in memory and have references to old models. Hence, Views are being displayed with stale data of the first user.
To better explain myself:
public class LoginViewModel : Screen
{
private User _model;
// Property gets initialised only on instantiation of ViewModel class
public User Model
{
get { return _model; }
set
{
if (Equals(value, _model)) return;
_model = value;
NotifyOfPropertyChange(() => Model);
}
}
// ViewModel constructor - instantiated once only by Caliburn on startup
public LoginHeaderViewModel(IAuthService authService)
{
Model = _authService.User;
}
}
When my app first launches, Caliburn will automatically instantiate LoginViewModel and run its constructor, which in turn fetches the current logged in user. After a user is logged out, and another user logs in, LoginViewModel is not instantiated again at runtime because it already exists. The Model property is not re-evaluated and hence the associated View is not told to refresh itself.
I tried re-creating Caliburn's WinRTContainer on logout but the application started behaving funny. I was suspicious of going down this approach anyway so didn't look too much into it just yet.
The way I resolved my initial problem (and there are many ways to do so) is using Caliburn event aggregation.
I first start off by creating an event:
public class LoginEvent
{
public bool IsloggedIn { get; set; }
public LoginEvent(bool isloggedIn)
{
IsloggedIn = isloggedIn;
}
}
When a user logs in, I make sure that I publish an instance of the login event:
public LoginViewModel(IEventAggregator events)
{
_events = events;
}
public async void SignIn()
{
// Do login logic ...
_events.Publish(new LoginEvent(true));
}
In the ViewModel that needs to rest itself whenever a new user logs in, ensure that I subscribe to the LoginEvent to reinitialise my models when it is triggered. A ViewModel listening to events must implement the IHandle<T> interface.
public class DependentViewModel : IHandle<LoginEvent>
{
public DependentViewModel(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
InitialiseViewModel();
}
public async void InitialiseViewModel()
{
// Initialise all your model objects here...
}
public async void Handle(LoginEvent ev)
{
if (ev.IsloggedIn)
{
InitialiseViewModel();
}
}
}
What Lifestyle do your ViewModel objects have?
There are also override methods such as OnDeactivate which you could use to clean up user specific settings, take a look here specifically at the screens section for methods that you can override.
I'm looking for a way to do a "Touch" command binding between axml and ViewModel, or some else like FocusChanged etc.
A simple "Click" command works fine like so:
local:MvxBind="{'Touch':{'Path':'CameraButtonCommand'}}" />
public IMvxCommand CameraButtonCommand
{
get
{
return new MvxRelayCommand(
() =>
{
RequestNavigate<AugRealityViewModel>(true);
})
;
}
}
However, I've tried other event types for the controll(in this case it's ImageButton) and they are not being processed. When I've checked the events listings in the View Class I see those:
public event EventHandler Click;
public event EventHandler<View.CreateContextMenuEventArgs> ContextMenuCreated;
public event EventHandler<View.FocusChangeEventArgs> FocusChange;
public event EventHandler<View.KeyEventArgs> KeyPress;
public event EventHandler<View.LongClickEventArgs> LongClick;
Only Click event has the general EventHandler attached to it, while other have genericed EventHandlers, and I'm wondering if that's the reason why it doesn't work.
I've also tried to attach a method to those events in the View class getting the proper control by FindViewById method and it works as expected this time around. But somehow I can't do it in the axml through Commands.
Also one more thing. The "Click" event is sending the "EventArgs" object as one of the parameters, and also the object reference. I can see that with ease if I do this behaviour in View Class, but when I do this by binding, I don't see those arguments when I'm processing the Command in ViewModel.
The framework can automatically bind any events which require EventHandler types. However, for any events which require a templated EventHandler (with custom EventArgs) then you are correct - you'll need to include a custom Binding.
The good news is that custom bindings are easy to write and to include.
For example, to bind:
public event EventHandler<View.LongClickEventArgs> LongClick;
you can include something like:
public class LongPressEventBinding
: MvxBaseAndroidTargetBinding
{
private readonly View _view;
private IMvxCommand _command;
public LongPressEventBinding(View view)
{
_view = view;
_view.LongClick += ViewOnLongClick;
}
private void ViewOnLongClick(object sender, View.LongClickEventArgs eventArgs)
{
if (_command != null)
{
_command.Execute();
}
}
public override void SetValue(object value)
{
_command = (IMvxCommand)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_view.Click -= ViewOnLongClick;
}
base.Dispose(isDisposing);
}
public override Type TargetType
{
get { return typeof(IMvxCommand); }
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
Which can be configured in setup using something like:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<View>("LongPress", view => new LongPressEventBinding(view)));
}
Note that you can't write a single class that binds to all the different event types - as the compiler requires you to include the correct Type for the EventArgs. However, you could fairly easily change public class LongClickEventBinding to something like public class CustomEventBinding<TViewType, TEventArgsType> if you wanted to.
With regards to what argument you should pass into the IMvxCommand Execute method, I guess this depends a bit on the method in question, and it also depends on whether you need the ViewModel to support multiple platforms, or whether it is just for Android.
I am trying to configure an application such that types from assemblyA can be used by my console to allow for logging in an AOP style. The JournalInterceptor will just write out method calls, input and maybe output arguments to a log file or datastore of some kind.
I can register one type at a time but I would like to register all types in one go. Once I get going I may add some filtering to the registered types but I am missing something.
I am trying to use Classes.FromAssemblyContaining but am not sure how to get at an IRegistration instance for the call to WindsorContainer::Register
Any clues?
// otherAssembly.cs
namespace assemblyA
{
public class Foo1 { public virtual void What(){} }
public class Foo2 { public virtual void Where(){} }
}
// program.cs
namespace console
{
using assemblyA;
public class JournalInterceptor : IInterceptor {}
public class Program
{
public static void Main()
{
var container = new Castle.Windsor.WindsorContainer()
.Register(
Component.For<JournalInterceptor>().LifeStyle.Transient,
// works but can't be the best way
Component.For<Foo1>().LifeStyle.Transient
.Interceptors<JournalInterceptor>(),
Component.For<Foo2>().LifeStyle.Transient,
.Interceptors<JournalInterceptor>(),
// how do I do it this way
Classes.FromAssemblyContaining<Foo1>()
.Pick()
.LifestyleTransient()
.Interceptors<JournalInterceptor>()
);
Foo1 foo = container.Resolve<Foo1>();
}
}
}
Implement a Pointcut. In Castle Windsor this is done by implementing the IModelInterceptorsSelector interface.
It would go something like this:
public class JournalPointcut : IModelInterceptorsSelector
{
public bool HasInterceptors(ComponentModel model)
{
return true; // intercept everything - probably not a good idea, though
}
public InterceptorReference[] SelectInterceptors(
ComponentModel model, InterceptorReference[] interceptors)
{
return new[]
{
InterceptorReference.ForType<JournalInterceptor>()
}.Concat(interceptors).ToArray();
}
}
Then register the Interceptor and the Pointcut with the container:
this.container.Register(Component.For<JounalInterceptor>());
this.container.Kernel.ProxyFactory.AddInterceptorSelector(new JournalPointcut());
For in-depth explanation, you may want to see this recording.