I have a simple app that consists of:
Model
Items
Filter criteria applied to that list of items
Views
WelcomePage
MainItemsPage
FilterEditPage
I am using MVVM Light and Windows Phone 7
I currently have 3 ViewModels, one for each View. In the past I have had a single ViewModel which made the comunication which I am about to ask about very easy. However I wanted to go with the 3 seperate VMs as that seems to be the correct way.
The WelcomePage is able to set one of the Filter criteria before navigating to the MainItemsPage. The MainItemsPage is bound to an Items property that is exposed by its ViewModel. That ViewModel needs to have filtered that list depending on the current filter criteria. The FilterEditPage allows the user to edit the full criteria set of 4 variables. When the criteria is changed the Items collection used in the ViewModel for MainItemsPage needs to be refiltered.
The question is how I flow the Filter changes through the app. I know that MVVM has the concept of Messaging and the MVVM Light toolkit provides the Messenger class. However what I am struggling with is where does the responsibility lie for sending those messages?
Do the 3 VMs go to the Model whenever they need to work with the current Filter set?
Do all Filter updates go through the FilterEditViewModel and that in turn broadcasts a filter change message?
Do I go back to a single VM for all the Views?
I cannot see 1. working because something will need to trigger the VMs to go back to the Model
I know I can get 3. working right now with no problem. Is it that wrong?
TIA
Pat Long
I would put the shared current filter in the Model not the view model. You've got lots viewModels potentially on different pages or on the same page (consider a breadcrumb showing current selection and something else that needs to show a filter has been applied).
How about a singleton model for the Filter that view models can subscribe to?
Three VMs is the right way in your scenario. I suggest you to build a Parent/Child relation between you VMs. Since the the MainVM holds the ItemList, this is the place, where FilterChanges are applied. The FilterEditVM only receives the filter changes and than calls the MainVM, that it has to re-apply the filters.
The structure would be something like this:
public class WelcomePageVM
{
public WelcomePageVM()
{
this.FilterEditPageVM = new FilterEditPageVM(this);
this.MainItemsVM = new MainItemsVM(this);
}
public FilterEditPageVM FilterEditPageVM { get; private set; }
public MainItemsVM MainItemsVM { get; private set; }
public void SetInitialFilter1(object filter)
{
// the initial filter
this.FilterEditPageVM.Filter1Value = filter;
this.MainItemsVM.ApplyFilters();
}
}
public class FilterEditPageVM : ChildViewModelBase<WelcomePageVM>
{
public FilterEditPageVM(WelcomePageVM parent)
: base(parent) { }
public object Filter1Value { get; set; }
public object Filter2Value { get; set; }
public object Filter3Value { get; set; }
public object Filter4Value { get; set; }
public void FinishFilterChange()
{
this.Parent.MainItemsVM.ApplyFilters();
}
}
public class MainItemsVM : ChildViewModelBase<WelcomePageVM>
{
public MainItemsVM(WelcomePageVM parent)
: base(parent) { }
public List<object> ItemList { get; set; }
public void ApplyFilters()
{
// filter apply logic
}
}
public abstract class ChildViewModelBase<T>
{
T _parent;
public ChildViewModelBase(T parent)
{
this._parent = parent;
}
public T Parent { get { return _parent; } }
}
Here you can access all viewmodels, which is okay because you stay in the "controller" level.
Related
I have found a solution that works (using DTOs and AutoMapper), which is reproduced below, but I would prefer an answer that lists the different approaches to the problem with examples and this will be marked as the answer if received.
In my entity model I have a navigation property that goes from a child entity to the parent entity. My project was working swimmingly. Then I began to use AutoFixture for unit testing, and testing failed, AutoFixture saying I had a circular reference.
Now, I realise that circular reference navigation properties like this are OK within Entity Framework, but I found this post (Use value of a parent property when creating a complex child in AutoFixture), where Mark Seemann, the creator of AutoFixture states:
"For the record, I haven't written an API with a circular reference for years, so it's quite possible to avoid those Parent/Child relations."
So, I want to understand HOW a domain model can be refactored to avoid child/parent relations.
Below are the entity classes in question, the repository method, and how I use the property causing the circular reference in my View. The perfect answer would explain the different options I could choose from with examples, and the basic pros/cons of each approach.
Note: The property causing the circular reference is User, in the UserTeam model.
Models:
public class UserProfile
{
public UserProfile()
{
UserTeams = new HashSet<UserTeam>();
Games = new HashSet<Game>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<UserTeam> UserTeams { get; set; }
public virtual ICollection<Game> Games { get; set; }
}
public class Game
{
public Game()
{
UserTeams = new HashSet<UserTeam>();
}
public int Id { get; set; }
public int CreatorId { get; set; }
public virtual ICollection<UserTeam> UserTeams { get; set; }
}
public class UserTeam
{
public UserTeam()
{
UserTeam_Players = new HashSet<UserTeam_Player>();
}
public int Id { get; set; }
public int UserId { get; set; }
public int GameId { get; set; }
public virtual UserProfile User { get; set; }
public virtual ICollection<UserTeam_Player> UserTeam_Players { get; set; }
}
Repository Method
public IEnumerable<Game> GetAllGames()
{
using (DataContext)
{
var _games = DataContext.Games
.Include(x => x.UserTeams)
.Include(x => x.UserTeams.Select(y => y.User))
.ToList();
if (_games == null)
{
// log error
return null;
}
return _games;
}
}
View
#model IEnumerable<Game>
#foreach (var item in Model){
foreach (var userteam in item.UserTeams){
<p>#userteam.User.UserName</p>
}
}
Now, if I remove the 'User' navigation property, I wouldn't be able to do '#userteam.User.UserName'
So, how do I refactor the domain model to remove the circular reference, whilst being able to easily loop through Games, and do something like
UserTeam.User.Username?
I had a similar problem with AutoFixture and EntityFramework a while ago. My solution was to add an extension to AutoFixture, that allows you to build a SUT with a few recursions. That extension has recently been adopted in AutoFixture.
But I understand that your question was not about how to make AutoFixture construct recursive data structures, which is indeed possible, but how to create domain models without recursion.
First, you have tree or graph structures. Here anything but recursion would mean indirection through loose coupled node ids. Instead of defining an association, you would have to traverse the tree query-by-query or cache the whole thing and traverse by node-key lookup, which may be impractical depending on the tree-size. Here it is very convenient to make EF do the work for you.
The other common structure is a two-way navigational structure similar to your user / game scenario. Here it is often not that inconvenient to prune the navigation flow to a single direction. If you omit one direction, say from game to team, you can still easily query all teams for a given game. So: User has a list of games and a list of teams. Team has a list of games. Games have no navigational reference to either. To get all users for a specific game you could write something like:
var users = (from user in DataContext.Users
from game in user.Games
where game.Name == 'Chess'
select user).Distinct()
I have found a solution that works (using DTOs and AutoMapper), which is reproduced below, but I would still prefer an answer that lists the different approaches to the problem with examples, in particular whether this is a desirable solution, or whether I should stick with the navigation properties as they were, get rid of AutoFixture, and when it comes to serializing for json just utilise other work arounds (attributes etc)...
So, in my View Model, I added a couple of classes:
public class GameDTO
{
public int Id { get; set; }
public int CreatorId { get; set; }
public ICollection<UserTeamDTO> UserTeamsDTO { get; set; }
}
public class UserTeamDTO : UserTeam
{
public UserProfile User { get; set; }
}
And in my controller, I use AutoMapper to map the Game / UserTeam objects from the repository to my DTO objects, and return the IList _gamesDto to the View.
var _games = _gameRepository.GetAllGames();
IList<GameDTO> _gamesDto = new List<GameDTO>();
IList<UserTeamDTO> _userteamsDto = new List<UserTeamDTO>();
GameDTO _gameDto = new GameDTO();
UserTeamDTO _userteamDto = new UserTeamDTO();
Mapper.CreateMap<Game, GameDTO>();
Mapper.CreateMap<UserTeam, UserTeamDTO>();
foreach (Game _game in _games)
{
foreach (UserTeam _userteam in _game.UserTeams)
{
_userteamDto = Mapper.Map<UserTeamDTO>(_userteam);
_userteamDto.User = _userRepository.GetUser(_userteam.UserId);
_userteamsDto.Add(_userteamDto);
}
_gameDto = Mapper.Map<GameDTO>(_game);
_gameDto.UserTeamsDTO = _userteamsDto;
_gamesDto.Add(_gameDto);
}
I had a similar problem recently which also impacted serializing JSON objects. I decided to remove the circular references from my data model.
I first removed the redundant navigation properties which were creating the circular references. I made sure that my resulting tree of data made sense. This allowed me to make it clear which objects own which relationships.
This also made EF unable to automatically reason about my relationships. I had to specify the One-to-Many and Many-to-Many relationships using the FluentAPI. I found a solution here: https://stackoverflow.com/a/16719203/1887885
Hope this is helpful.
My question is the same as this one
However, I don't really see a solution there. Lets say I have a simple model with two POCOs, Country and State.
public class Country
{
public string Code { get; set; }
public string Name { get; set; }
}
public class State
{
public string Code { get; set; }
public string Name { get; set; }
public virtual Country Country { get; set; }
}
When I use the repository to .GetStateByCode(myCode), it retrieves a dynamic proxy object. I want to send that over the wire using a WCF service to my client. The dynamic proxy is not a know type so it fails.
Here are my alternatives. I can set ProxyCreationEnabled to false on the context and then my .GetStateByCode(myCode) gives me a POCO which is great. However, the navigation property in the POCO to Country is then NULL (not great).
Should I new up a state POCO and manually populate and return that from the dynamic proxy that is returned from the repository? Should I try to use AutoMapper to map the dynamic proxy objects to POCOs? Is there something I'm totally missing here?
I think the answer from Ladislav Mrnka is clear. The Warnings Still apply. Even with this idea below. Becareful what gets picked Up. He just didnt include , if you want to proceed how to easily get data from Object a to object B. That is question at hand really.
Sample solution
See nuget package ValueInjecter (not the only tool that can do this... but very easy to use)
it allows easy copying of One object to another especially with the same properties and types.
( remember the lazy loading / navigation implications).
So vanilla option is :
var PocoObject = new Poco();
PocoObject.InjectFrom(DynamicProxy); // copy contents of DynamicProxy to PocoObject
but check the default behaviour and consider a custom rule
var PocoObject = new Poco();
PocoObject.InjectFrom<CopyRule>(DynamicProxy);
public class CopyRule : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
bool usePropertry; // return if the property it be included in inject process
usePropertry = c.SourceProp.Name == "Id"; // just an example
//or
// usePropertry = c.SourceProp.Type... == "???"
return usePropertry;
}
}
I want to create a specific class to manage a collegion in my application.
For example, I have a Store and I have a list of customers in a collection, in this collection I have a customer that is the customer of the month, and some customers that got a prize to get a discount for some reason. Let's get to the code:
public class Store {
public ICollection<Customer> Customers { get; set; }
public Customer CustomerOfTheMonth
{
//get and set for customer of the month
}
public ICollection<Customer> DiscountCustomers
{
//get and set for customer of the month
}
public ICollection<Customer> GetAllCustomers
{
//get and set for customer of the month
}
}
But in my database, I only have two tables. Store, and Customer.
What I want to do is create a specific collection for the customer, to remove the logic from the Store and put in a specific class, after all, I don't feel that the logic belongs to neither of those classes.
I wans tomething like this:
public class Store {
internal CustomerCollection Customers { get; set; }
//get and set for the propertis, just delegating for the collection
}
public class CustomerCollection {
public ICollection<Customer> Customers { get; set; }
public ICollection<Customer> DiscountCustomers
{
//get and set for customer of the month
}
//get and set with logic to filter the collection
}
Is there away to create this mapping and keep with only two tables in the database? I want to make it transparent to the application. Sorry for the code problems, typed in stack overflow and didn't check the syntax.
don't need to create your business logic to your model classes. Separate your logic to upper level. Here are your model classes. It will create your relationships as you want
public class Store {
public vertual ICollection<Customer> Customers { get; set; }
//get and set for other propertis
}
public class Customer{
//get and set for other propertis
}
Create a repository or service layer to apply specific business logic ,
public ICollection<Customer> GetDiscountCustomers()
{
return dbContext.Customers.where(c=>c.discount=true).ToList()
}
if you want to load stores with customers at once you can use eager loading,
public ICollection<Store> GetAllStores()
{
return dbContext.Stores.Include("Customers").ToList()
}
I am new to MVC and using the repository pattern in an attempt to select data containing two objects to return to a strongly typed viewmodel,
Im getting a bit stuck with what the best way to do this is,
The two tables are related by a customer id field, i have a repository interface set up, and a display template that is strongly typed to a viewmodel that contains properties for the Customer and a Customer Site object, all i need is to display a list of customer sites along with the relevant customer name from the customers table.
In the display template i have the following
<%= Html.DisplayFor(x => x.Customers.CustomerName) %>
<%= Html.DisplayFor(x => x.Customers.Site.AddressLine1) %>
I have this display template working but my model is empty.
Where im getting confused is how to define this data in the interface and repository, and how to return the data to my model, to simply return my list of customers i use this in my interface
IQueryable<Customer> Customers { get; }
Then a simple LINQ select.
But as this data will contain both customers and customer sites im unsure how to define this in the interface?
Also will a LINQ join be a suitable method to return this data to my viewmodel? something like
var Customers =
from c in customers
join cs in customerSites on c equals cs.CustomerId into ps
from p in ps
select new { Customer = c, cs.CustomerName };
UPDATE=========
This is the code i am using in the view model that is stronly typed to the display template,
public class CustomerViewModel
{
public int Id { get; set; }
public string CustomerName { get; set; }
public string PrimaryContactName { get; set; }
public SiteViewModel Site { get; set; }
}
Its how to populate the model in the repository/controller with both objects to display in a list that im struggling with.
You may have done soem of the following steps already so please ignore if you have..
1 creat a ViewModel folder in your solution.
2 Create a base view model .... might look like this ->
public class BaseViewModel
{
public PageProperties PageProperties { get; set; }
public User User { get; set; }
}
3 Setup a view model for your controller action maybe like so ->
public class ProjectVM : BaseViewModel
{
public ProjectPoco project { get; set; }
}
4 In your controller get your data from your repositiory and pass it to an instance of your view model like this ->
var contextVM = new ProjectVM();
contextVM.project = ObjectExtensions.Convert<ProjectPoco>(tbl.Single(id));
contextVM.PageProperties = new PageProperties
{
Heading = contextVM.project.Name,
SubHeading = "Details for" +
contextVM.project.Name
};
return View(contextVM);
5 set your views model to be that of your view model ->
#model
NerveCentre.ViewModels.ProjectVM
6 use your viewmodel to pull data out into your view ->
#Model.project.Description
A quick and rough description of passing data to your view via a view model. hope I didnt miss anything out.
As for the data.. looking at how you have the model (Customers.Site.AddressLine1) would it not just be possible to pass the Customers from your query to your view model?
So your viewmodel might look something like..
public class SomeViewModel: BaseViewModel
{
public List<Customer> Customers { get; set; }
}
If you let us know what you are using for data access then we might be able to help more with the specifics of getting the data out of your tables and into the format you want?
I have a strong type view of type
List<List<MyViewModelClass>>
The outer list will always have two lists of List<MyViewModelClass>. For each of the two outer lists I want to display a group of checkboxes. Each set can have an arbitrary number of choices.
My view model class looks similar to this:
public class MyViewModelClass
{
public Area Area { get; set; }
public bool IsGeneric { get; set; }
public string Code { get; set; }
public bool IsChecked { get; set; }
}
So the final view will look something like:
Please select those that apply:
First set of choices:
x Option 1
x Option 2
x Option 3
etc.
Second set of choices:
x Second Option 1
x Second Option 2
x Second Option 3
x Second Option 4
etc.
Checkboxes should display MyViewModelClass.Area.Name, and their value should be related to MyViewModelClass.Area.Id. Checked state is of course related to MyViewModel.IsChecked.
Question
I wonder how should I use Html.CheckBox() or Html.CheckBoxFor() helper to display my checkboxes? I have to get these values back to the server on a postback of course.
I would like to have my controller action like one of these:
public ActionResult ConsumeSelections(List<List<MyViewModelClass>> data)
{
// process data
}
public ActionResult ConsumeSelections(List<MyViewModelClass> first, List<MyViewModelClass> second)
{
// process data
}
If it makes things simpler, I could make a separate view model type like:
public class Options
{
public List<MyViewModelClass> First { get; set; }
public List<MyViewModelClass> Second { get; set; }
}
As well as changing my first version of controller action to:
public ActionResult ConsumeSelections(Options data)
{
// process data
}
Generally I create a type (Model) that "models" the view that I want to create.
i.e.
public class FooViewModel
{
public List<Option> General { get; set; }
public List<Option> Others { get; set; }
//Some Methods and other properties
}
public FooController :...
{
private FooViewModel fooViewModel = new FooViewModel();
}
Edit:
Have a look at this post, as it's exactly what you want!
Solution
It turns out this is not so complicated at all. All you have to do is name your controls appropriately and everything will be bound together as it should be. So. Whatever your controls, they should always be named like:
name="first[0].propName"
name="first[1].propName"
name="first[2].propName"
name="first[3].propName"
...
// or
name="first[0].data[0].property"
name="first[0].data[1].property"
name="first[0].data[2].property"
...
name="first[1].data[0].property"
name="first[1].data[1].property"
...
All these will get bound to List<SomeType> first controller action parameter (the second one has another collection inside a collection).
Very important note
If you add/remove these controls dynamically using Javascript, you have to make sure, that indexes are consecutive starting from 0, and not having any gaps. And you will have a nice working app with dynamic number of elements in a collection.
You can read about it in my blog post as well.