I want to build a native mobile app the adds products to the cart using Intershop's REST API. That part is easy. However, for the checkout I would like to use the standard responsive web application. Any suggestions how these two approaches can be mixed elegantly?
I solved it now by writing a little pipelet, that attaches the basket created via REST API to the current session.
public class AttachBasketToSession extends Pipelet
{
#Inject
CurrentApplicationBOProvider currentApplicationBOprovider;
#Override
public int execute(PipelineDictionary aPipelineDictionary) throws PipeletExecutionException
{
String basketUUID = aPipelineDictionary.get("BasketUUID");
String userID = aPipelineDictionary.get("UserID");
StorefrontSession session = aPipelineDictionary.get("CurrentSession");
ApplicationBO applicationBO = currentApplicationBOprovider.get();
BasketBORepository basketBORepository = applicationBO.getRepository("BasketBORepository");
UserBORepository userBORepository = applicationBO.getRepository("UserBORepository");
BasketBO basketBO = basketBORepository.getBasketBO(basketUUID);
UserBO userBO = userBORepository.getUserBOByID(userID);
// Set the current user as owner of the new basket
basketBO.setUserBO(userBO);
// Assign the basket to the session
Map<String, String> basketUUIDs = (Map)session.getObject("BasketUUIDs");
for (String key : basketUUIDs.keySet())
{
basketUUIDs.put(key, basketBO.getID());
break; // Assume there is only one basket attached to the session
}
session.putObject("BasketUUIDs", basketUUIDs);
return PIPELET_NEXT;
}
}
Related
We are transitioning from Xamarin.Forms to .Net MAUI but our project uses Prism.Unity.Forms. We have a lot of code that basically uses the IContainer.Resolve() passing in a collection of ParameterOverrides with some primitives but some are interfaces/objects. The T we are resolving is usually a registered View which may or may not be the correct way of doing this but it's what I'm working with and we are doing it in backend code (sometimes a service). What is the correct way of doing this Unity thing in DryIoC? Note these parameters are being set at runtime and may only be part of the parameters a constructor takes in (some may be from already registered dependencies).
Example of the scenario:
//Called from service into custom resolver method
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", IObjectWithData)
};
//Custom resolver method example
var resolverOverrides = new List<ResolverOverride>();
foreach(var parameterOverride in parameterOverrides)
{
resolverOverrides.Add(parameterOverride);
}
return _container.Resolve<T>(resolverOverrides.ToArray());
You've found out why you don't use the container outside of the resolution root. I recommend not trying to replicate this error with another container but rather fixing it - use handcoded factories:
internal class SomeFactory : IProductViewFactory
{
public SomeFactory( IService dependency )
{
_dependency = dependency ?? throw new ArgumentNullException( nameof(dependency) );
}
#region IProductViewFactory
public IProductView Create( int productID, IObjectWithData objectWithData ) => new SomeProduct( productID, objectWithData, _dependency );
#endregion
#region private
private readonly IService _dependency;
#endregion
}
See this, too:
For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.
For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.
For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.
Also, be aware of potential subtle differences in container behaviours: Unity's ResolverOverride works for the whole call to resolve, i.e. they override parameters of dependencies, too, whatever happens to match by name. This could very well be handled very differently by DryIOC.
First, I would agree with the #haukinger answer to rethink how do you pass the runtime information into the services. The most transparent and simple way in my opinion is by passing it via parameters into the consuming methods.
Second, here is a complete example in DryIoc to solve it head-on + the live code to play with.
using System;
using DryIoc;
public class Program
{
record ParameterOverride(string Name, object Value);
record Product(int productID);
public static void Main()
{
// get container somehow,
// if you don't have an access to it directly then you may resolve it from your service provider
IContainer c = new Container();
c.Register<Product>();
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", "blah"),
};
var parameterRules = Parameters.Of;
foreach (var po in parameterOverrides)
{
parameterRules = parameterRules.Details((_, x) => x.Name.Equals(po.Name) ? ServiceDetails.Of(po.Value) : null);
}
c = c.With(rules => rules.With(parameters: parameterRules));
var s = c.Resolve<Product>();
Console.WriteLine(s.productID);
}
}
I have a project consisting of 2 parts:
ASP.NET API using Entity Framework
.NET MAUI Client App
I use DTOs for comunication from/to the API in order not to expose other properties of my entities. Thanks to this approach I was able to separate Entity data and data that are sent from the API.
At first I used these DTOs also in the MAUI UI. But after some time I started to notice that they contains UI-specific properties, attributes or methods that have no purpose for the API itself, so they are redundant in requests.
EXAMPLE:
1 - API will receive request from MAUI to get exercise based on it's name
2- ExerciseService returns: ExerciseEntity and ExerciseController use AutoMapper to Map ExerciseEntity -> ExerciseDto ommiting ExerciseId field (only admin can see this info in the DB) and returning it in the API response
3 - MAUI receives from the API ExerciseDto. But in the client side it also want to know if data from ExerciseDto are collapsed in the UI. So because of that I add IsCollapsed property into the ExerciseDto. But now this is a redundant property for the API, because I dont want to persist this information in the database.
QUESTIONS:
Should I map these DTOs to new objects on the client side ?
Or how to approach this problem ?
Is there an easier way how to achieve the separation ?
Because having another mapping layer will add extra complexity and a lot of duplicate properties between DTOs and those new client objects.
Normally if you use clean architecture approach your DTOs shoud contain no attributes and other specific data relevant just for some of your projects, to be freely usable by other projects in a form of dependency.
Then you'd have different approaches to consume DTOs in a xamarin/maui application, for example:
APPROACH 1.
Mapping (of course) into a class that is suitable for UI. Here you have some options, use manual mapping, write your own code that uses reflection or use some third party lib using same reflection. Personally using all of them, and when speaking of third party libs Mapster has shown very good to me for api and mobile clients.
APPROACH 2.
Subclass DTO. The basic idea is to deserialize dto into the derived class, then call Init(); if needed. All properties that you manually implemented as new with OnPropertyChanged will update bindings after being popupated by deserializer/mapper and you alse have a backup plan to call RaiseProperties(); for all of the props, even thoses who do not have OnPropertyChanged in place so they can update bindings if any.
Example:
our Api DTO
public class SomeDeviceDTO
{
public int Id { get; set; }
public int Port { get; set; }
public string Name { get; set; }
}
Our derived class for usage in mobile client:
public class SomeDevice : SomeDeviceDTO, IFromDto
{
// we want to be able to change this Name property in run-time and to
// reflect changes so we make it bindable (other props will remain without
// OnPropertyChanged BUT we can always update all bindings in code if needed
// using RaiseProperties();):
private string _name;
public new string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged();
}
}
}
// ADD any properties you need for UI
// ...
#region IFromDto
public void Init()
{
//put any code you'd want to exec after dto's been imported
// for example to fill any new prop with data derived from what you received
}
public void RaiseProperties()
{
var props = this.GetType().GetProperties();
foreach (var property in props)
{
if (property.CanRead)
{
OnPropertyChanged(property.Name);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public interface IFromDto : INotifyPropertyChanged
{
//
// Summary:
// Can initialize model after it's being loaded from dto
void Init();
//
// Summary:
// Notify all properties were updated
void RaiseProperties();
}
We can get it like: var device = JsonConvert.DeserializeObject<SomeDevice>(jsonOfSomeDeviceDTO);
We then can call Init(); if needed..
Feel free to edit this answer to add more approaches..
My aim is to access bindable property across the the App. But My current framework ViewModel Instance create multiple time
My Requirement : I have the cart count in the bottomTray(CheckuoutViewModel) i want to increase the cart count any where in the app page, but in this cart count not update when back click, its only working on forward navigation, the reason behind CheckoutViewModel instance create each and every time. so that i'm try to instant creation at earlier.
Here I'm list out sample ViewModel and calling method
Login ViewModel
Checkuout ViewModel(This view model common for all page)
BaseNavigationViewModel(Its BaseViewModel)
As of now i'm calling when BindinContext each and every time like,
new LoginViewMode(navigation)
new CheckoutViewModel(navigation)
what will do to create all ViewModel instance when app start time like ViewModel Locator?
Im tried
public static ViewModelLocator Locator
{
get { return locator ?? (locator = new ViewModelLocator()); }
}
And ViewModel Locator
public ViewModelLocator()
{
navigation = App.Current.MainPage.Navigation;
}
internal CustomTabBarViewModel CustomTabBarVM
{
get
{
return customTabBarVM ?? (customTabBarVM = new CustomTabBarViewModel(navigation));
}
}
And CustomTabBar.xaml.cs
public CustomTabBar()
{
viewModel = App.Locator.CustomTabBarVM;
InitializeComponent();
BindingContext = viewModel;
}
and Expectation
App.Locator.CustomTabBarVM.BadgeCartCount = OrderObject.Instance.ORDER_OBJECT.Items.Count;
This approach is working fine but it's create some navigation issues
A singleton instance is a common feature of virtually all MVVM frameworks (Prism, FreshMVVM etc). If you aren't using a framework (if you aren't, I would STRONGLY advise you consider using one), below is a solution.
To obtain a single instance of a ViewModel you can use the App class to host the object and access it whenever you need.
Create a public static property of your ViewModel:
public static MyViewModel MyViewModelInstance { get; }
Create an instance in the constructor of the app
public App()
{
InitializeComponent();
MyViewModelInstance = new MyViewModel();
var myPage = new MyPage()
{
BindingContext = MyViewModelInstance
};
var navPage = new NavigationPage(myPage);
MainPage = navPage;
}
Whenever you create a new page, access the shared instance
// This method is just an example of how you might create a new page and wire up the view model
async void GoNextClicked(System.Object sender, System.EventArgs e)
{
var myPage = new MyPage()
{
BindingContext = App.MyViewModelInstance
};
await this.Navigation.PushAsync(myPage);
}
This approach comes with a few caveats, you are creating instances when the app loads not when they are needed (Eagerly loading). So a performance optimisation would be to use Lazy<T> to handle the creation of these objects. However this is logic that has already been written for you in MVVM frameworks, they are there to help you and you should be using them.
Lazy Load
You can save memory and performance at startup by lazy loading the viewmodel, here is this example rewritten to support this pattern:
public static MyViewModel MyViewModelInstance
{
get => _myViewModelInstanceFactory.Value;
}
private static Lazy<MyViewModel> _myViewModelInstanceFactory = new Lazy<MyViewModel>(() => new MyViewModel(), true);
public App()
{
InitializeComponent();
var myPage = new MyPage()
{
BindingContext = MyViewModelInstance
};
var navPage = new NavigationPage(myPage);
MainPage = navPage;
}
Now this object won't be created until it is accessed by your code, and once it has been accessed once it has already been created and will go on to live in memory for the rest of your apps lifecycle.
Axemasta has good answer about re-use of a shared view model instance.
I'll give an alternative approach to the underlying need given in one comment: how to have a static property (so the value is common), and Bind to it when Binding to an instance.
Use this approach if you do want a different CheckoutViewModel for each new page. For example, if there are other properties that should be set up differently, depending on the page.
public class CheckoutViewModel : : INotifyPropertyChanged // or your MVVM library's base class for ViewModels.
{
public static int SharedCount { get; set; }
public void IncrementCount()
{
Count = Count + 1;
}
public int Count {
get => SharedCount;
set {
// Exact code might be simpler if using an MVVM library.
if (SharedCount != value)
{
SharedCount = value;
OnPropertyChanged("Count");
}
}
}
}
}
LIMITATION: This assumes that only the current instance of CheckoutViewModel is visible; if you need to "notify" OTHER Views (update other CheckoutViewModel instances), then you'll need a "publish/subscribe" solution. In Xamarin Forms, one such solution is MessagingCenter.
I have two ViewModels, MainWindowShellViewModel(shellVm) and MainWindowContentViewModel(contentVm). The shellVm publishes an event and the contentVm subscribes to it.
The shell VM looks something like the following. I have omitted many details.
// ctor
public MainWindowShellViewModel(IEventAggregator eventAggregator)
{
_EventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(IEventAggregator) + " service injected is null!!!");
_AppStartingClosingEventToken = _EventAggregator.GetEvent<AppStartingClosingEvent>();
}
private void MainWindowShellLoaded()
{
var payload = new AppStartingClosingEventData();
payload.Data = "MainWindowStarting";
_AppStartingClosingEventToken.Publish(payload);
}
The AppStartingClosingEvent is a no brainer type as follows.
public class AppStartingClosingEvent : PubSubEvent<AppStartingClosingEventData>
{ }
public class AppStartingClosingEventData
{
public string Data { get; set; }
}
And finally, the contentVm looks as follows.
public MainWindowContentViewModel(IEventAggregator eventAggregator)
{
_AppClosingEventToken.Subscribe(AppStartingClosing);
}
private void AppStartingClosing(AppStartingClosingEventData appStartingClosingEventData)
{
if (appStartingClosingEventData.Data == "MainWindowStarting")
LoadState(appStartingClosingEventData);
if (appStartingClosingEventData.Data == "MainWindowClosing")
SaveState(appStartingClosingEventData);
}
I want to test the that the method AppStartingClosing inside of contentVm is called with proper data. I am using Moq
I am running out of ideas. Please suggest. Tried the following but so far no success.
How do I test Prism event aggregator subscriptions, on the UIThread?
Using Moq to verify a Prism event subscription fails
Unit testing with Moq, Prism 6, and Event Aggregation
Moq Event Aggregator Is it possible
// Verifying a delegate was called with Moq
EDIT
Here is what I have tried.
// Arrange
var mockingKernel = new MoqMockingKernel();
var eventAggregatorMock = mockingKernel.GetMock<IEventAggregator>();
var eventBeingListenedTo = new AppStartingClosingEvent();
eventAggregatorMock.Setup(e => e.GetEvent<AppStartingClosingEvent>()).Returns(eventBeingListenedTo);
var vm = mockingKernel.Get<MainWindowContentViewModel>();
var evData = new AppStartingClosingEventData();
evData.Data = "MainWindowStarting";
// Act
eventBeingListenedTo.Publish(evData);
Now, what should I do? I am not even clear if I have approached correctly.
Now what should I do?
After eventBeingListenedTo.Publish(evData); look whether whatever effect SaveState should have is actually happening.
I am not even clear if I have approached correctly.
You do not want to test whether one method in a class is called by another method of that class.
So instead of trying to do
subjectUnderTest.DoStuff();
MagicallyVerifyThatThisGotCalled( () => subjectUnderTest.SomeEffect() );
you should do
var subjectUnderTest = new SubjectUnderTest( serviceMock.Object );
subjectUnderTest.DoStuff();
serviceMock.Verify( x => x.SomeEffectOnTheService(), Times.Once );
Assert.That( subjectUnderTest.SomePropertyThatsChanged, Is.EqualTo( newValue ) );
Whatever SubjectUnderTest does internally to achieve the desired effect, is not in the scope of the test. It's private to SubjectUnderTest, you don't care how it is done as long as it is done at all. When testing, look at the externally visible state of your subject under test, and what it does to its dependencies.
I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}