I have two screens which need to manage a list of items. The user can navigate from 1st screen to 2nd screen.
When user navigates back from 1st screen, the list of items needs to be saved to a store.
The way I was thinking to do it having a service holding the collection which gets injected in the view-models.
But the service instance must be released once user navigates back from 1st screen:
// somewhere, maybe in Application:
LifetimeManager = new ContainerControlledLifetimeManager();
Container.RegisterType<Service>(lifetimeManager);
class FirstPage()
{
ContainerControlledLifetimeManager _lifetimeManager;
public FirstPage()
{
InitializeComponent();
}
void OnDisappearing()
{
LifetimeManager.RemoveValue();
}
}
But this seems awkward, there has to be a better way to do this...
I see there's a ExternallyControlledLifetimeManager which seems it's what I need, but it would still require for FirstPage instance to get the instance of the manager and call RemoveValue. Or, I would call GC.Collect on OnDisappearing to make sure the GC collects the instance of the service, and ExternallyControlledLifetimeManager will recreate it next time it needs to be injected in the FirstPageViewModel. But calling GC.Collect seems wrong too...
Based on your comments, it sounds like you should be passing your data to your views via navigation parameters. Then each page can handle the parameters differently. Keep it simple.
var p = new NavigationParameters();
p.Add("items", MyListOfItems);
_regionManager.RequestNavigate("MyView", p);
Put your Service in another service whose job is to provide the current instance of Service.
interface IServiceProvider
{
Service CurrentInstance { get; set; }
}
Then register this one as a singleton (ContainerControlledLifetimeManager) and use it in both view models. The first one sets the instance to a new one, most likely created by some IServiceFactory, and the second one fills the instance with data.
Related
Suppose I want a UI where users in a group have a synchronized view. Imagine a group-chat application, where everyone in the group should see the exact same messages.
When a user posts a message to the group, the entire group needs to receive it.
With JS, I might use SignalR Groups, and have the front-end generate a SignalR event with a message is posted. The server-side hub would then send that message out to the group.
On Server-Side Blazor, since all the users' states are already on the server, how would I coordinate updating the UI of groups of users on Blazor?
I was trying to achieve something similar. After a while, I discovered a way of doing this. Kyle's answer helped me figure it out.
You must create a class, let's call Messenger, which has an Action property, and a method that invokes this Action once called
public Action OnMessage { get; set; }
public void AddMessage(string message)
{
Text = message;
OnMessage?.Invoke();
}
In the razor component, inject the Messenger. Also, you should set an event to the OnMessage property, and then call StateHasChanged in order to tell the view that it can update
#inject Messenger messenger
// rest of component here, and then the code block
protected override async Task OnInitializedAsync()
{
messenger.OnMessage += () =>
{
// do something with the messenger.Text here
InvokeAsync(this.StateHasChanged);
};
}
Don't forget, on your Startup class, add the class Messenger as a Singleton in order to have it available for injection
services.AddSingleton<Messenger>();
I hope that is what you were trying to achieve, and it can help others as well.
Cheers.
I have a page object framework and would like to reduce the amount of code in each class. Each class has a function that takes in test data and determines what to do with it (enter data on page, click something etc.) I have #FindBy for the web elements I will be using. My question is, should I also have a method for each action that I will do on each web element?
For example, I have a web element for entering a username, should I have a corresponding method called enterUsername(String username)?
Currently I feel like this will cause bloat and there is no reason the test can't operate on WebElements themselves but I am looking for examples that would push my decision one way or the other.
N/A
public class Page1
#FindBy
WebElement userName;
public void enterUsername(String s) {
username.sendKeys(s);
}
or
#Test
public void someTest() {
//go to page1
page1.userName.sendKeys("username123");
}
Results should be the same either way. What I am interested in is maintainability and cleanliness of the code.
You should go for option 1 (I would add clear() function call before typing just in case)
With regards to the "each action" - you should create a method for each user scenario for the user visiting this or that page you're going to automate.
The main idea of the Page Object Model design pattern is to provide an abstraction layer between your test logic and the DOM of the application under test
So your test would look like:
#Test
public void loginUsingCorrectCredentials() {
page1.enterCredentials("admin", "secret");
Assert.assertTrue(page1.isUserLoggedIn(),"Failed to login");
}
#Test
public void loginUsingInCorrectCredentials() {
page1.enterCredentials("admin", "badpassword");
Assert.assertFalse(page1.isUserLoggedIn(),"Failed to login");
}
So keep in mind 2 main principles:
Your tests should exactly represent real life business use cases and the functions you create at the page should match all scenarios you're going to cover
Keep UI-related stuff in Page Objects and Test Logic in test code, to wit no WebElements in test cases and no Assertions in Page Objects
I'm beginning to work on the caching infrastructure for my ASP.NET MVC site. The problem is, I can't seem to find a reasonable place for data caching (other than 'everywhere')
Right now my architecture looks like this:
Controller -> Service Layer -> Repository. The repository uses Linq to SQL for data access.
The repository exposes generic methods like Insert, GetById, and GetQueryable, which returns an IQueryable that the service layer can further refine.
I like the idea of putting caching in the repository layer, since the service layer shouldn't really care where the data comes from. The problem though is with cache invalidation. The service layer has more information about when data becomes stale than the repository. For instance:
Suppose we have a Users table and an Orders table (the canonical example). The service layer offers methods like GetOrder(int id), which would call the repository layer:
public Order GetOrder(int id)
{
using(var repo = _repoFactory.Create<Order>())
{
return repo.GetById(id)
}
}
or
repo.GetQueryable(order => order.Id == id && order.HasShipped == false).Single();
If we cache in the repository layer, it seems like it would be very limited in knowing when that order data has changed. Suppose the user was deleted, causing all their orders to be deleted with a CASCADE. The service layer could invalidate the Orders cache, since it knew the user was just removed. The repository though (since it's a Unit of Work), wouldn't be aware. (Ignore the fact that we shouldn't be querying orders for a deleted user, since it's just an example).
There's other situations where I think this shows itself. Suppose we want to fetch all the users orders:
repo.GetQueryable(order => order.UserId == userId).ToList()
The repository can cache the results of this query, but, if another order is added, this query is no longer valid. Only the service layer is aware of this though.
It's also possible my understanding of the repository layer is wrong. I sort of view it as a facade around the data source (i.e. changing from L2SQL to EF to whatever, the service layer is unaware of the underlying source).
Realistically, you will need another layer; the data caching layer. It will be used by your service layer when requesting data. Upon such a request, it will decide if it has the data in cache or if it needs to query the appropriate repository. Likewise, your service layer can tell this new data caching layer of an invalidation (the deletion of a particular user, etc.).
What this can mean for your architecture though, is that your data caching layer will implement the same interface(s) your repositories do. A fairly simple implementation would cache the data by entity type and key. However, if you are using a more sophisticated ORM behind the scenes (NHibernate, EF 4, etc.), it should have caching as an option for you.
You could put an event on the objects returned by your repositories, and have the repository subscribe the cache invalidation to a handler.
For example,
public class SomethingRepository{
public Something GetById(int id){
var something = _table.Single(x=>x.id==id);
something.DataChanged += this.InvalidateCache;
return something;
}
public void InvalidateCache(object sender, EventArgs e){
// invalidate your cache
}
}
And your Something object needs to have a DataChanged event and some public method for your service layer to call to trigger it. Like,
public class Something{
private int _id;
public int Id{
get { return _id; }
set {
if( _id != value )
{
_id = value;
OnDataChanged();
}
}
}
public event EventHandler DataChanged;
public void OnDataChanged(){
if(DataChanged!=null)
DataChanged(this, EventArgs.Empty);
}
}
So, all your service layer needs to know is that the data is being changed, and the repository handles the cache invalidation.
I also suggest you take ventaur's advice and put the cache invalidation logic in a separate service. You don't need to go so far as to create a separate "data caching layer", but the logic would be cleaner if kept in a different class.
Let's consider this page's code-behind:
public partial class Products : Page
{
private static SomeClass SharedField;
public Product()
{
// ... Some logic
}
}
Do all Products pages instances share the same SharedField, I know this is a basic concept of static fields. But in this case, really? all users can have access (and can't have their own instance of) to the same static field on the website-level?
If so, in what aspects this would used by the web developer? or is this non-recommended practice?
Yes, there will be a single instance of that static field for all users, but only within a single worker process. If you have web farms/web gardens, they will each have their own static instance. If the worker process restarts, you'll get a new static instance.
You'll have to use locking around that shared field to ensure thread safety.
As for why to use that, I'm not sure, I never do it. The best example I can give you is the built-in static HttpContext.Current, which gives you access to the Request, Response, etc.
SharedField will be available in one instance for the entire life-cycle of the web site.
To read a bit more about it, see this answer.
A better practice would be to store your object in the Application state.
Application["MyObject"] = new SomeClass();
You can get the name of a page within HttpContext via Request.Path.
Is there a way to distinguish between different requests from the same page?
That is when two different instances of yourpage.aspx make a request, how can you distinguish between the two using HttpContext?
you probably want to do this in a base Page class, but here's what i would do
public partial class Default : System.Web.UI.Page
{
private Guid _instanceID;
public Guid InstanceID
{
get { return _instanceID; }
}
/// <summary>
/// Constructor
/// </summary>
public Default()
{
this._instanceID = Guid.NewGuid();
}
}
then using the HttpContext somewhere else in your code...
if (HttpContext.Current.CurrentHandler is Default)
{
((Default)HttpContext.Current.CurrentHandler).InstanceID;
}
Nothing built into ASP.NET will allow you to differentiate different "page instances" or requests from them.
However, you can easily add a Guid to your view state to uniquely identify each page. This mechanism works fine when you are in the Page class itself. If you need to identify requests before you reach the page handler, you need to use a different mechanism (since view state is not yet restored).
The Page.LoadComplete event is a reasonable place to check if a Guid is associated with the page, and if not, create one.
If you're using authentication, would it work for you to distinguish which user submitted the page?
You could use System.Web.Httpcontext.Current.User.Identity.Name.
just throwing this out there: NInject (and other DI containers) use a scoping mechanism based on the HttpContext.Current object itself, so depending on what you're trying to do, you could attempt to retrieve a state object from the DI container and go from there.