By-request singleton initialization using IoC container in MVVM application - c#

My WPF application uses Caliburn.Micro as MVVM framework, Entity Framework as data access technology. The purpose of the application is to maintain database. There are multiple Views that consist of menu and result datagrid. With menu controls user specifies what DB data must be shown in results datagrid. Some of ComboBoxes in different Views binded to same properties List<EntityName> in their ViewModels.(Menus have same items to chose from). Each of this Lists loads from DB when it's ViewModel initialises. I don't want to load same Lists from DB for each View, I want make it once. For achieve this I have moved this List properties to another class MenuLists with PreloadLists() method to get lists from DB once, then injected this class as singleton to each ViewModel using Caliburn's built-it IoC container. Like this:
IoC container configuration:
container.PerRequest<KitchenOneViewModel>();
container.PerRequest<KitchenTwoViewModel>();
container.Singleton<IMenuLists, MenuLists>();
MenuLists class:
public class MenuLists : IMenuLists
{
public List<Meat> MeatLst { get; set; }
public List<Fish> FishLst { get; set; }
public void PreloadLists()
{
using (var db = new DBEntities())
{
MeatLst = db.Meat.OrderBy(x => x.MeatName).ToList();
FishLst = db.Fish.OrderBy(x => x.FishName).ToList();
}
}
}
Injecting it in ViewModel:
public KitchenOneViewModel(IMenuLists menuLists)
{
_menuLists = menuLists;
}
The problem is that I need to call MenuLists.PreloadLists() somehow, but if I do this in MenuLists consructor, I'll get long UI freeze in first ViewModel initialization, because constructor of MenuLists(with expensive PreloadLists() DB-access operation) will be called. I don't know what of Views will be opened first, so I can't call PreloadLists method from there. I need to preload my lists once without freezing UI. How can I resolve it? Maybe delay MenuLists initialization somehow?

Either lazily load each list individually to cut down on initialization time, something like:
//only initialized when it's used
private static Lazy<List<Meat>> meatList
= new Lazy<List<Meat>>(
() =>
{
//load single list here
}
);
public List<Meat> MeatList { get{ return meanList.Value; } }
...or if you want to initialize everything at once, do so when your application starts up versus waiting for the ViewModels to be instantiated.
Alternately, you could consider having the initialization be async if you don't need to block.

Related

In which class should wrapped EF entities be saved?

I have a WPF application which is written with an implementation of MVVM. There's no extra framework for the MVVM pattern.
My entities from EF db first are wrapped in their own viewmodels and I have a modelcontroller to load them into their viewmodels from a 'window' viewmodel.
Example of an entity viewmodel:
public class PurchaseOrderViewModel : ViewModels.ViewModelBase
{
private someType _prop;
public someType Prop
{
get
{
return _prop;
}
set
{
_prop = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public PurchaseOrderViewModel() {
// default constructor for LINQ
}
public PurchaseOrderViewModel(purchaseorder entity)
{
// load values from entity in properties
}
}
Example of a window viewmodel:
public class MainViewModel: ViewModels.ViewModelBase
{
private IModelController modelController = new ModelController();
private List<PurchaseOrderViewModel> _poList;
public List<PurchaseOrderViewModel> POList
{
get
{
return _poList;
}
set
{
_poList = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public MainViewModel()
{
POList = modelController.GetPurchaseOrders();
}
}
Example of ModelController:
public class ModelController : IModelController
{
public List<PurchaseOrderViewModel> GetPurchaseOrders()
{
using (var model = new DBContext())
{
return model.purchaseorders
.Select(new PurchaseOrderViewModel { /* assign properties */ })
.ToList();
}
}
}
Where am I supposed to save this wrapped viewmodel (PurchaseOrderViewModel) once the user is done editing it? As I see it, there are 2 options:
Create a save function in each viewmodel that points back to the modelController, but this feels like an inappropriate approach.
Create a save function in the modelcontroller and pass the viewmodel as an argument
It's most likely that I'm missing something in the MVVM pattern, but please point me in the right direction. Thank you!
EDIT: I excluded the view (MainView) from the info provided, but this view binds directly to the properties exposed by MainViewModel.
First up, I problably wouldn't name it ModelController as that's slightly confusing makes people think you are speaking MVC. Instead, if you call it xxxxService (e.g. PurchaseOrdersService) it makes more sense and it no longer feels "inappropriate" because having a VM delegate the actual work is what many users of IoC do. Plus it keeps your VM clean.
NOTE: By "service" I don't necessarily mean that your VM will be calling a WCF service directly (nor should you). Service is just a means to achieve something in an abstract and encapsulated way on behalf of clients. Examples include:
saving information to a DB
getting the current log mechanism
They can even be facades whereby they create a WCF client proxy and call a remote service on your behalf without you having to know the details.
So a typical flow is:
Command >> View code behind >> VM >> Service
The reason I include the view's code behind is that typically this is where you:
Catch exceptions
The starting point of async/await for asynchonous calls to your VM and service
Now when you pass context fromt the VM back to the service, there is no rule on what exactly you pass however I see no reason to pass VM to the service because that contains information the service doesn't care about.
Just pass the M which your VM should have bound to in the first place and continued to update via binding.

How to code/design the Model part in Caliburn.Micro?

I am new to MVVM pattern and Caliburn.Micro. I've read some tutorials on how to get started, but I'm confused about the Model part of MVVM in the context of Caliburn.
I want to create my first MVVM application and I have some design questions:
In tutorials, the Model was presented as simple property in
ViewModel. How should I manage more complex models? Is there any
naming convention? Obviously, there should be some external classes
made for my models, but how should I communicate between my models
and the view?
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be
visible from different ViewModels?
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Thanks in advance for any help :)
EDIT:-------------------------------------------------------
Thank you for your anwser. I uderstand the topic more clearly, but I'm still confused about some details.
For an example, let's assume this little application. I have a form that allows me to add a new Customer. It has a few fields like Name, Surname etc.
After pressing the button, I invoke the addCustomer command in the ViewModel. I want my program to store the newly created customer inside the database.
My view also has the List control (whatever), which displays my customers as raw strings (like "Name: John, Surname: Doe, Address: ..." I know it's dumb to make it like this, but i need an example of model manipulation (like .toString()))
For this example, I've created a bunch of stuff to illustrate my vision of that process:
fields - it's a set of form fields like Name, Surname etc.
customerSet - it's a set of Customer class to store all created
customers
.addToDatabase(fields) - a method which puts newly created customer
to the database
.getStrings - a method which prepares a set of strings to be
displayed by the list in CustomerView
I think about 2 approaches that would be good for a solution:
First approach. I don't like this one. The only advantage is, that
ViewModel handles all the logic inside application. Sharing model
would be a serious problem here, because saving methods are bound to
the ViewModel class.
Second, MVC like approach. To me it's the most intuitive one. But - I
don't know where should I store CustomersModel object, so few
ViewModels could have access to it.
Which is the better one? Or maybe another approach that is more suitable for MVVM?
Another problem is: Where should I put my method that will load all the Customers from the database, so they could be displayes on the list? In "get method" inside viewmodel, or inside a model class?
In tutorials, the Model was presented as simple property in ViewModel.
How should I manage more complex models? Is there any naming
convention? Obviously, there should be some external classes made for
my models, but how should I communicate between my models and the
view?
Your models should represent whatever it is they need to whether it's a customer, account, etc. The view models job is to handle the interaction between the view and models.
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Generally, you will map complex models to more friendly format for display, you can do it manually or use a tool like AutoMapper.
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be visible
from different ViewModels?
If you're working with a local db you can pass IDs around. If it's a service you could persist the model locally for other view models to work with. You could also inject a singleton, ISharedData, into view models that need to work with shared data.
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Create services for more complex model manipulation / business logic. Inject the services into view models that require them. ICustomerService, IAccountService, etc.
EDIT:-------------------------------------------------------
You're first approach is correct. To your point about sharing the model being a serious problem because saving methods are bound to the view model class. The view model will have a SaveCustomerCommand that is fired when the button is clicked, because of its binding.
The SaveCustomerCommand will persist the CustomerModel, regardless of how the CustomerModel is persisted. So if its a database, the view model might have a reference to a context and issue a _db.Save(CustomerModel). If another view model needs to manipulate a CustomerModel, it will do so by using the context. The view model could also have a reference to a CustomerService that handles the crud for the CustomerModel.
Here's how this might look:
public class AddCustomerViewModel : Screen
{
private readonly ICustomerService _customerService;
public AddCustomerViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
//If button is named x:Name="SaveCustomer" CM will
//bind it by convention to this method
public void SaveCustomer(Customer customer)
{
_customerService.Save(customer);
}
}
public class CustomerListViewModel : Screen
{
private readonly ICustomerService _customerService;
private List<CustomerDisplayModel> _customers;
public CustomerListViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
public List<CustomerDisplayModel> Customers
{
get { return _customers; }
set
{
_customers = value;
NotifyOfPropertyChange();
}
}
//only fires once, unlike OnActivate()
protected override void OnInitialize()
{
var customers = _customerService.LoadAllCustomers();
//could just use the model but this shows how one might map from
//the domain model to a display model, AutoMapper could be used for this
Customers = customers.Select(c => new CustomerDisplayModel(c)).ToList();
}
}
public interface ICustomerService
{
List<Customer> LoadAllCustomers();
void Save(Customer customer);
}
//same as button, Label named x:Name="CustomerName" will bind
// to CustomerName
public class CustomerDisplayModel
{
private readonly Customer _customer;
public CustomerDisplayModel(Customer customer)
{
_customer = customer;
}
public string CustomerName
{
get { return _customer.Name; }
set { _customer.Name = value; }
}
public string Surname
{
get { return _customer.Surname; }
set { _customer.Surname = value; }
}
public string Address
{
get { return _customer.Address; }
set { _customer.Address = value; }
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Address { get; set; }
}

How to make grid operations work with custom property in EF?

In a ASP.NET MVC 5 web site I have a GridView using the devexpress component binding using the LINQ method.
EF generated a partial class to map a table that i use to display in that gridview.
In this partial class generated by the EF i have a ID_Status property wich has a corresponding description in other table. I made another partial class to deal with this custom Property and it works ok, except when i try to make a 'Sort' operation clicking on the header of this column.
The partial class generated by the EF.
[Table("Test")]
public partial class Test
{
[Key]
public long ID_Test { get; set; }
public long ID_TestStatus { get; set; }
//other properties
}
My Custom partial class:
public partial class Test
{
private static readonly TestRepository _testRepository;
static TestRepository()
{
_testRepository= new TestRepository();
}
public string StatusDescription
{
get { return _testRepository.GetStatusDescriptionById(ID_TestStatus); }
}
}
When i try to Sort using another column it works fine, but when i try to Sort using the custom property Column all the grid cell values gets empty, without any value.
Any suggestion?
It's not a very good idea to have data access code inside an entity. One reason is that it makes it very hard to write unit test. Another reason is that it is very likely to give rise to the n + 1 anti pattern. In your case, it does: one (1) query to get the Tests, then each Test (n) sends a separate query to the database to get its StatusDescription.
The way you implemented it also raises some eyebrows, because
_testRepository is static, which meas there is probable some context instance living for the entire lifecycle of the application - unless GetStatusDescriptionById creates a new context for each call, but that wouldn't be a good idea either.
The GetStatusDescriptionById call is made each time the property is accessed. In a web application this may not be a big problem because the objects are newly created each time they are requested anyway, but in other environments this could be highly inefficient.
A better approach would be to fetch the Testss with their Status included:
context.Tests.Include(t => t.TestStatus)
and have an unmapped property like
public string StatusDescription
{
get { return TestStatus== null ? string.Empty : TestStatus.Description; }
}
better still (in my opinion) would be not to show Test objects directly, but TestDto objects like
public class TestDto
{
public string StatusDescription { get; set; }
//other properties that match Test's properties
}
and use a tool like AutoMapper to map a collection of Tests to TestDtos. If Test has a property Status and TestStatus has a property Description, AutoMapper will be able to flatten that into StatusDescription automatically.
Both this StatusDescription property and the Dto appraoch set the state of a Test(Dto) object once. I don't think any grid component can mess with that.

View an Already Instantiated ViewModel in MVVMCross

Does anyone know how to view an existing IMvxViewModel?
In my app, I have already created a bunch of ViewModels (PhotoViewModel) inside of another view model. They exist as a property on the parent ViewModel (AlbumViewModel). It would be very nice to just show a particular instance of a PhotoViewModel instead of creating a new instance of that view model when I want to view it.
public class AlbumViewModel : MvxViewModel {
public ObservableCollection<PhotoViewModel> Photos
{
get { return GetValue(() => Photos); }
set { SetValue(value, () => Photos); }
}
}
public class PhotoViewModel : MvxViewModel { }
I was wondering if there was a way, other then creating my own IMvxViewModelLocator, to accomplish this task. I think having a protected method on the MvxNavigationObject called View could be really helpful both for new developers using the framework as well as performance. We'd be able to skip all of the reflection done currently to instantiate a view model.
The default ShowViewModel mechanism in MvvmCross uses page-based navigation - this navigation has to use Uris on WindowsPhone and Intents on Android.
Because of this, MvvmCross does not allow navigation by 'rich' objects - simple serialisable POCOs are Ok, but complicated 'rich' objects are not supported.
This is further essential because of 'tombstoning' - if your app/page/activity is later rehydrated then you cannot be sure of what historic View or ViewModel objects are actually in your history "back" stack.
If you want to navigate by rich object then the best way is to store those rich objects in a lookup service and to then navigate by some key/index into the lookup. However, I would personally call those lookedup objects Models rather than ViewModels (but the boundary does sometimes become blurred!)
Although based on MvvmCross v1 code, this question still gives quite a good background to this - What is the best way to pass objects to "navigated to" viewmodel in MVVMCross?
Some more up-to-date explanations include:
How to pass data across screens using mvvmcross
Custom types in Navigation parameters in v3
https://github.com/slodge/MvvmCross/wiki/ViewModel--to-ViewModel-navigation (under construction)
One final thing....
... the MvvmCross manifesto insists that MvvmCross is very open to customisation ...
Because of this you can override MvvmCross navigation and view model location if you want to. To do this, creating your own IMvxViewModelLocator would probably be a good way to start.
After some testing, below is a proposed solution. I'm not 100% in love with it, but it does work and provide the type developer experience I was looking for. So lets dig in.
To start, all of my ViewModels (VM) inherit from a base VM, AVM. This abstract base class supports looking up of an object as a public static method. It's a little gross, but it works well if you're willing to sip on the Kool-Aid. Below is the portion of the class that's relevant to this problem:
public abstract class AVM : MvxViewModel {
private static readonly Dictionary<Guid, WeakReference> ViewModelCache = new Dictionary<Guid, WeakReference>();
private static readonly string BUNDLE_PARAM_ID = #"AVM_ID";
private Guid AVM_ID = Guid.NewGuid();
private Type MyType;
protected AVM()
{
MyType = this.GetType();
ViewModelCache.Add(AVM_ID, new WeakReference(this));
}
public static bool TryLoadFromBundle(IMvxBundle bundle, out IMvxViewModel viewModel)
{
if (null != bundle && bundle.Data.ContainsKey(BUNDLE_PARAM_ID))
{
var id = Guid.Parse(bundle.Data[BUNDLE_PARAM_ID]);
viewModel = TryLoadFromCache(id);
return true;
}
viewModel = null;
return false;
}
private static IMvxViewModel TryLoadFromCache(Guid Id)
{
if (ViewModelCache.ContainsKey(Id))
{
try
{
var reference = ViewModelCache[Id];
if (reference.IsAlive)
return (IMvxViewModel)reference.Target;
}
catch (Exception exp) { Mvx.Trace(exp.Message); }
}
return null;
}
protected void View()
{
var param = new Dictionary<string, string>();
param.Add(BUNDLE_PARAM_ID, AVM_ID.ToString());
ShowViewModel(MyType, param);
}
In order to get this all wired up, you have to create a custom view model locator. Here's the custom locator:
public class AVMLocator : MvxDefaultViewModelLocator
{
public override bool TryLoad(Type viewModelType, IMvxBundle parameterValues, IMvxBundle savedState, out IMvxViewModel viewModel)
{
if (AVM.TryLoadFromBundle(parameterValues, out viewModel))
return true;
return base.TryLoad(viewModelType, parameterValues, savedState, out viewModel);
}
}
Lastly you have to wire up. To do so, go into your App.cs and override CreateDefaultViewModelLocator like so:
protected override IMvxViewModelLocator CreateDefaultViewModelLocator()
{
return new AVMLocator();
}
You're all set. Now in any of your derived ViewModels that are already alive and well, you can do the following:
myDerivedVM.View();
There's still some more I need to do (like making sure the WeakReferences do their job and I don't have memory leaks and some additional error handling), but at the very least it's the experience I was going for. The last thing I did was add the following command to the AVM base class:
public MvxCommand ViewCommand
{
get { return new MvxCommand(View); }
}
Now you can bind that command to any UI object and when invoked, it'll launch that view with that very instance of the VM.
Stuart, thanks for your help in steering me in the right direction. I'd be interested in hearing your feedback on the solution I provided. Thanks for all of your work with MVVMCross. It really is a very beautiful bit of code.
Cheers.

How to store data or persist it across forms

I have created a program with Windows Forms in C# and the architecture is something like this
* BaseClass.cs
* EntityClass.cs
* ControllerClass.cs
* DataAccessClass.cs
* Viewer.cs
So basically the Entity class inherits from the base class, and the data access class goes and retrieves data from a MySql database.
What I want to do is to be able to use the data pulled without having to call the data access class if I already pulled it once.. where can I put this data so I can access it anywhere?..
I was reading about serializing into memory.. but I want to get a good advise here on where to put it and if possible how. I would really appreciate any pointers.
Regards
Seems like you want to continue with the MVC pattern, and introduce a "Model" component that is shared between your 2 views. The Model would be initialized/refreshed using your DataAccessClass. Each form (or View) should have a reference to the Model. I would not suggest using serialization as you are just sharing an in-memory C# object in the same process between 2 different Forms.
For example:
public class MyFirstView : Form
{
private ModelClass m_model;
public MyFirstView(ModelClass model)
{
m_model = model;
m_model.OnDataRefresh += this.Model_OnDataRefresh;
}
}
public class MySecondView : Form
{
private ModelClass m_model;
public MySecondView(ModelClass model)
{
m_model = model;
m_model.OnDataRefresh += this.Model_OnDataRefresh;
}
}
public class ModelClass
{
private DataAccessClass m_dataAccess;
public event EventHandler OnDataRefresh = {}; // fired when data is refreshed
public void EnsureDataIsLoaded(); // queries the db if we haven't already
public void RefreshData(); // refreshes the data from the db
public IList<Entity> GetDataList(); // access to data items
}
For each Form/View, you can use the Form.Load event to refresh the view with the model data. Perhaps you could have a method on the ModelClass.EnsureDataIsLoaded() that will use the DataAccessClass to query the database if the you haven't already.
And lastly, if the model changes, you need some way to push the changes to the view(s). One way to do this is have the model fire an event when the data is refreshed, and each view subscribes to that event.
I would suggest using a static class to hold the data. If you will have multiple forms opened simultaneously, you could have a Dictionary within the static class keyed by some form identifier.
You shouldn't need to serialize anything, you can just hold on the to the references in some sort of cache layer.
Assuming that your data access classes take some sort of query as a parameter, you can store the results in a dictionary keyed by the query. Then you check the cache first for query results, if not there go to the database, retrieve it, and cache it. If you are using lambda expressions for your queries, you can call the .GetHashCode() method to get a unique hash for that lambda.
EX:
IDictionary<string, IEnumerable<T>> Cache;
IEnumerable<T> GetData<T>(string query)
{
var key = typeof(T).Name + query;
if (!this.Cache.ContainsKey(key))
{
// get from database
var data = SomeRepository.GetData(query);
this.Cache[key] = value;
}
return this.Cache[key]
}
I suggest you to take a look at the System.Runtime.Caching.MemoryCache class. It might serve your need.

Categories