MVVM: Delete a CustomerViewModel, but how to get the Customer model inside it? - mvvm

I have a list of CustomerViewModels in a ComboBox. The selected CustomerViewModel I want to delete and also the Customer wrapped inside it to remove it from the repository.
But how can I access the Customer model inside the CustomerViewModel?

Just a suggestion, make your collection of customerviewmodels an ObserableCollection of CustomerViewModels.
what this buys you is a CollectionChanged Event that you could listen on with a delegate for changes to the collection ie deletion, so from there you could manipulate you model accordingly
http://msdn.microsoft.com/en-us/library/ms653375(VS.85).aspx
perhaps something like
public class CustomersViewModel: ViewModelBase
{
public ObservableCollection<CustomersViewModel> Customers { get; private set; }
public CustomersViewModel()
{
Customers = new ObservableCollection<CustomersViewModel>(GetCustomers());
Customers.CollectionChanged +=
(sender, args) =>
{
if (args.Action == NotifyCollectionChangedAction.Remove)
{
foreach (CustomerViewModel customerViewModel in args.NewItems)
{
DeleteCustomer(customerViewModel.Customer);
}
}
};
}
private void DeleteCustomer(Customer customer)
{
// Call into your repo and delete the customer.
}
private List<CustomersViewModel> GetCustomers()
{
// Call into your model and return customers.
}
... ICommands ect...
}

You might have already access to the Customer inside CustomerViewModel (the VieModel needs to expose the properties of the Customer so the View can databind on them; I usually do it by exposing the Customer or a copy of it directly).
The point is that you should not delete the Customer yourself. That's what the ViewModel is for, to expose an ICommand that deletes the associated Customer. Depending on which MVVM framework you are using, look into DelegateCommand or another equivalent.
Your CustomerViewModel would have a
public ICommand DeleteCommand { get; private set; }
and your View would bind a CommandTarget (probably a Button) to this command. When the command is executed a private method of CustomerViewModel will be run, and you can delete the Customer from there without exposing the deletion mechanism to other parts of the code. For example:
public CustomerViewModel()
{
this.DeleteCommand = new DelegateCommand(this.ExecuteDeleteCommand);
}
private void ExecuteDeleteCommand()
{
// remove the Customer from the ObservableCollection of customers
// and also delete it from the database, or do anything else you want
}

Related

Having an ICollection with references only instead of creatiing copies/clones

I have a Survey that contains questions and also which users can/will participate in the survey.
like so
public virtual ICollection<User> ParticipatingUsers { get; set; }
public virtual ICollection<Question> SpecificQuestions { get; set; }
However, due to the ajaxy solution I create the questions first and then simply send in the ID of my created question with the survey data. So all I need to do is change the sortingIndex of the question and then add a reference to it in my Survey.
When it comes to users they belong to a Company entity and I only want to reference them from the survey not own them.
But currently I get all the id's for questions and users in my action method (.net mvc) and so currently I load all questions and users and attach them to my survey entity before sending the survey to the repository.
But when my Repository calls Add on dbset it clones the user and question data instead of simply referencing existing data.
I am lost, I have solved this exact problem for a normal navigation property by adding [Foreignkey] but i don't know how that would work with ICollection
For completeness
Here is my action method recieving the data
[HttpPost]
[FlexAuthorize(Roles = "RootAdmin")]
public ActionResult SaveSurvey(EditSurveyViewModel editModel)
{
if (!ModelState.IsValid)
{
//We dont bother to send this in so we need to fetch the list again
editModel.CompanyList = _companyRepository.GetAll();
List<string> deletionList = new List<string>();
//We clear out all questions from the state as we have custom logic to rerender them with the correct values
foreach (var modelstateItem in ModelState)
{
if (modelstateItem.Key.StartsWith("Questions"))
{
deletionList.Add(modelstateItem.Key);
}
}
foreach (string key in deletionList)
{
ModelState.Remove(key);
}
return View("EditSurvey", editModel);
}
List<Question> questionlist = new List<Question>();
int sort = 1;
Question q;
//We have questions sent in from the ui/client
if (editModel.Questions != null)
{
//Go trough each questions sent in
foreach (var question in editModel.Questions)
{
//if it's a page break, just assign our new question the sent in one and set sort index
if (question.IsPageBreak)
{
q = question;
q.SortIndex = sort;
}
else
{
//It's a question and all questions are already created with ajax from the client
//So we simply find the question and then set sort index and tie it to our survey
q = _questionRepository.GetById(question.Id);
q.SortIndex = sort;
}
questionlist.Add(q);
sort++;
}
}
//assign the new sorted questions to our Survey
editModel.Item.SpecificQuestions = questionlist;
List<User> userlist = new List<User>();
foreach (int id in editModel.SelectedUsers)
{
userlist.Add(_userRepository.GetById(id));
}
editModel.Item.ParticipatingUsers = userlist.ToList();
_surveyRepository.SaveSurveyBindAndSortQuestionsLinkUsers(editModel.Item);
return RedirectToAction("Index");
}
Here is the viewmodel the method gets sent in
public class EditSurveyViewModel
{
public Survey Item { get; set; }
public IEnumerable<Question> Questions { get; set; }
public bool FullyEditable { get; set; }
public IEnumerable<Company> CompanyList { get; set; }
public IEnumerable<int> SelectedUsers { get; set; }
}
Finally here is the repo method (so far i only implemented insert, not update)
public void SaveSurveyBindAndSortQuestionsLinkUsers(Survey item)
{
if (item.Id == 0)
{
Add(item);
}
ActiveContext.SaveChanges();
}
Update/Edit
Moho: You are of course correct, I think to my shame I was testing some things and forgot to reset the method before pasting it in here.
I have updated the action method above.
Slauma: Sorry for lack of details, here comes more.
All my repositories look like this
public class EFSurveyRepository : Repository<Survey>, ISurveyRepository
So they inherit a generic repository and implement an interface
The generic repository (the part we use in code above, looks like this)
public abstract class Repository<T> : IRepository<T> where T : class
{
public EFDbContext ActiveContext { get; private set; }
private readonly IDbSet<T> dbset;
public Repository()
{
this.ActiveContext = new EFDbContext("SurveyConnection");
dbset = ActiveContext.Set<T>();
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
public virtual T GetById(int id)
{
return dbset.Find(id);
}
I have noticed in the database that my User table (for User entity) now contains a Survey_Id field which i do not want it to have. I want a many-to-many where many surveys can link to many users (the same users) but the users should entity-wise still only belong to a Department in a Company.
Also, right now when I run the code (after I corrected my action method) I get the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
No InnerException, only that when i try to add the new survey.
The problem is that you are using separate contexts per repository:
public Repository()
{
this.ActiveContext = new EFDbContext("SurveyConnection");
//...
}
In your POST action you have four repositories in place: _companyRepository, _questionRepository, _userRepository and _surveyRepository. It means you are working with four different contexts, i.e. you load data from different contexts, create relationships between entities that are attached to different contexts and save the data in yet another context.
That's the reason for the entity duplication in the database, for the "multiple instances of IEntityChangeTracker" exception and will be the source for many other problems you might encounter in future.
You must refactor the architecture so that you are using only one and the same context instance ("unit of work") in every repository, for example by injecting it into the constructor instead of creating a new one:
private readonly EFDbContext _activeContext;
private readonly IDbSet<T> _dbset;
public Repository(EFDbContext activeContext)
{
_activeContext = activeContext;
_dbset = activeContext.Set<T>();
}
You build up questionList, set it to editModel.Item.SpecificQuestions, then overwrite the reference by settting that same property to editModel.Questions.ToList(), which is from your view model (i.e.: not loaded via your database context like questionList's question objects) and therefore appears to be new questions to your database context,
editModel.Item.SpecificQuestions = questionlist;
// what is this? why?
editModel.Item.SpecificQuestions = editModel.Questions.ToList();
Edit after question update:
Instead of using questionList and assigning to the questions property of the Survey, simply use the property directly.
Also, do you realize that if you're reusing Question records from the DB for multiple Surveys, you're updating the sort order at the question itself and not simply for that Survey? Each time you save a new survey that reuses questions, other surveys' question ordering will me altered. Looks like you need a relationship entity that will map Questions to Surveys where you can also store the sort order so that each survey can reuse question entities without messing up existing surveys question ordering.

Entity Framework / MVC Remove Item from Collection

What are some ways I can delete an item from a collection? (I am using MVC 4 and EF.)
As an example:
public class Birthday
{
public string Name { get; set; }
public virtual ICollection<Gift> Gifts { get; set; }
}
public class Gift
{
public string Name { get; set; }
public double Price { get; set; }
}
I'm using Editing a variable length list, ASP.NET MVC 2-style to create a dynamic list of Gifts.
The example is shows how to "Delete" a row. This will delete the row from the page and the correct Gifts are sent to the controller.
When I update the Birthday / Gifts everything new is updated properly, but anything deleted is still there.
So my question is what are some preferred ways to remove Gifts?
Two ways I've thought of already:
Get a Birthday from the DB and compare the Gifts removing as needed. I don't love this idea because it seems heavy handed.
Use WebApi / Ajax and delete the Gift from the list and the DB when the user pushes the delete link. I like this better than #1 but does this put too much business logic in the presentation layer?
I'm guessing that other people have had this similar problem and have a clever solution I haven't thought of yet.
Thanks in advance!
Make a Gifts api controller.
Let it have a Delete method accepting an Id of whatever type your Id is.
And do something like this in it:
public class GiftsController: ApiController
{
public void Delete(Guid Id)
{
var context = new MyContext();
var giftToDelete = context.Gifts.FirstOrDefault(g=> g.Id == Id);
if(giftToDelete != null)
{
context.Gifts.Remove(giftToDelete);
context.SaveChanges();
}
}
}
Make sure you make a DELETE request to this api in your JS delete function.
You may also replace the body of this method with some Service.DeleteGift(Id) if you're too concerned about doing things in the right place.
Like this:
public class ValuesController : ApiController
{
private List<string> list = new List<string>{"Item1","Item2","Item3","Item4","Item5"};
// DELETE api/values/5
public List<string> DeleteItem(int id)
{
list.Remove(list.Find((i => i.ToString().Contains(id.ToString()))));
return list;
}
}

Entity Framework 4.1 Implement INotifyPropertyChanged for POCO objects

I am implementing code first, MVC pattern and using Entity Framework 4.1. I put my problems in bold.
Lets assume (for simplify) that I have the following POCO object (Department) and I would like to know when it changes once contextDB.SaveChanges() is carried on and update it, so I implement the following:
public class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, e);
}
}
[Key(), Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
private string name;
[Required]
public string Name
{
get
{
return this.name;
}
set
{
if (this.name== value)
{
return;
}
this.name= value;
this.NotifyPropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
private string personInCharge;
[Required]
public string PersonInCharge
{
get
{
return this.personInCharge;
}
set
{
if (this.personInCharge== value)
{
return;
}
this.personInCharge= value;
this.NotifyPropertyChanged(this,
new PropertyChangedEventArgs("PersonInCharge"));
}
}
}
I have 3 projects(class libraries), for M(Model), V(View) and C(Controller).
From view(V), user generates and event, for example, adding a departament by pressing a button so the View which references the Controller(C), calls a method "Add" in the controller.
The controller(C) which references the Model(M), has access to the context because it instantiates the class which derives from dbContext in the Model, and through the context updates the entity "Department" by doing for example dbContext.departments.add(newDepartment).
When entity departments is updated in the model, NotifyPropertyChanged described above in the entity Department is raised but my problem starts here and is: how to say to the View, hey! entity departments has changed so view departments should be updated!
To achieve it, I have implemented observer pattern, I mean the view department which has a method called "Update", is attached to a collection of observers maintained by the model so the model, on a property change, iterates over this collection and call method "Update" for each view and the view updates.
My problem here is: I do not know how to subscribe to event PropertyChanged in the above class department from the view in order to once a property is changed in Department POCO object (class describe above), the model iterates over the observer collection that contains the observers attached and then calls the appropriate "Update" method for each view(observer) attached to the collection. or maybe it there another better way to do it rather than using INotifyPropertyChanged for POCO objects?
Also I see a problem of using INotifyPropertyChanged, I mean, for example, each time a depatment is added, the view will be updated twice as NotifyPropertyChanged is raised twice, one from Name property and another from PersonInCharge property. Another problem here: How to raise only one time the NotifyPropertyChanged event instead of twice?

GenericRepository TEntity change attribute value

I am using EF 5.0 and the model first approach. I have build a GenericRepository that has the basic get, insert, delete etc statements. Like:
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
My EF entities all have the attributes Modified and ModifiedBy. Now I want to change this values everytime I save an entity.
Is it possible to modify this two attributes (set the value) without writing an specific implementation all the time?
Thank you
I see two options for you to do this, but they both entail either introducing a base type or an interface for all of your entities to cover them in a generic function. I would prefer an interface, although each entity would have to implement it again and again.
Let's say you create
interface IAuditable
{
DateTime Modified { get; set; }
string ModifiedBy {get; set; } // User id?
}
Now you can do:
public virtual void Insert(TEntity entity)
where TEntity : IAuditable
{
entity.Modified = DateTime.Now;
entity.ModifiedBy = ???? // Whatever you get the name from
...
}
(Same for edit)
You can also subscribe to the context's SavingChanges event:
// In the constructor:
context.SavingChanges += this.context_SavingChanges;
private void context_SavingChanges(object sender, EventArgs e)
{
foreach (var auditable in context.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
.Select(entry => entry.Entity)
.OfType<IAuditable>)
{
auditable.Modified = DateTime.Now;
auditable.ModifiedBy = ????;
}
}
If you work with DbContext you can get to the event by
((IObjectContextAdapter)this).ObjectContext.SavingChanges
I'd like to add that more reliable time tracking can (and maybe should) be achieved by database triggers. Now you depend on a client's clock.
You can do this using the following code in your all methods of repository where you want to.
public virtual void Edit(TEntity entity)
{
entity.Modified=DateTime.Now;
entity.ModifiedBy=User.Identity.Name;
//Other saving to repository code
}

MVVM - Deciding which ViewModel is responsible for what

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.