I have a list item which has more or less around 10 objects. I could able to detect which item is selected and also I sending this item properties into the DetailViewModel,I am using messageprotocol in mvvmcross.
I could able to observe changes in the MainViewModel when user enters new value in the edittext in DetailViewModel.
I wonder how I am going to put these values back into the selected item and update list.
MainViewModel
private readonly IMvxMessenger _messenger;
private readonly MvxSubscriptionToken _token;
private MainViewModel _selectedItem;
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ShowViewModel<DetailViewModel>(
new DetailViewModel.Parameter
{
Age= _selectedItem.Age,
Category = _selectedItem.Category,
});
RaisePropertyChanged(() => SelectedItem);
}
}
public MainViewModel(IMvxMessenger messenger) {
_messenger = messenger;
_token = messenger.Subscribe<SelectedItemMessage>(OnMessageReceived);;
}
private void OnMessageReceived(SelectedItemMessage obj)
{
// I could observe the DetailView Changes in the MainViewModel
// I wonder how to put these value back to selectedItem
double? Age = obj.Age;
int? Category= obj.Category;
}
public virtual ICommand ItemSelected
{
get{ return new MvxCommand<TestViewModel>(item =>{ SelectedItem = item;});
}
}
private ObservableCollection<TestViewModel> _testViews;
private ObservableCollection<WellTestViewModel> _allTestItemViews;
public void Init(string Id)
{
List<Test> allTests = new List<Test>();
allTests = _TestService.GetAllTestById(Id);
foreach (var test in allTests)
{
_testViews.Add(TestViewModel.CreateViewModel(test, this));
}
_allTestItemViews = _testViews;
}
TestViewModel
public static TestViewModel CreateViewModel(Test entity, MainViewModel parent = null)
{
if (entity == null)
{
return null;
}
return new TestViewModel(parent)
{
Age = entity.Age,
Category= entity.Category,
};
}
public TestViewModel()
{
// parameterless constructor
}
readonly MainViewViewModel _mainViewModel ;
public TestViewModel(MainViewViewModel mainViewViewModel)
{
_mainViewModel = mainViewViewModel;
}
DetailViewModel
private readonly IMvxMessenger _messenger;
public class Parameter
{
public double? Age{ get; set; }
public int? Category { get; set; }
}
public void Init(Parameter param)
{
Age= param.Age;
Category= param.Category;
}
public DetailViewModel(IMvxMessenger messenger) {
_messenger = messenger;
}
public void UpdateMethod() {
var message = new SelectedItemMessage(this, age, category);
_messenger.Publish(message, typeof(SelectedItemMessage));
}
SelectedItemMessage
public SelectedItemMessage(object sender, double? age, int? category) : base(sender)
{
Age = age;
Category = category;
}
public double? Age { get; set; }
public int? Category{ get; set; }
}
Just use your _selectedItem and set the properties on it.
private void OnMessageReceived(SelectedItemMessage obj)
{
_selectedItem.Age = obj.Age;
_selectedItem.Category= obj.Category;
}
You need to update the collection inside the OnMessageReceived method:
var item = _allTestItemViews.FirstOrDefault(i => i.Id == id);
if (item != null)
{
item.Age = age;
item.Category = category;
}
You need to add Id to your model class so that you can uniquely identify the item you need to update.
Related
I have a view which opens an idialog that I contain a datagrid. I select certain rows and send back those as a list to the view but all I search I just found ResultButton return for idialog. How I can send back a custom value like a list or anything else ?
My iDialog ViewModel :
public DelegateCommand<string> CloseCommand { get; set; }
public DelegateCommand<string> AttachCommand { get; set; }
private string _txtSearch;
public string txtSearch
{
get { return _txtSearch; }
set { SetProperty(ref _txtSearch, value); }
}
public List<GE_Drawing> SelectedDrawings { get; set; }
private ObservableCollection<GE_Drawing> _DrawingList= new ObservableCollection<GE_Drawing>();
public ObservableCollection<GE_Drawing> DrawingList
{
get { return _DrawingList; }
set { SetProperty(ref _DrawingList, value); }
}
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set { SetProperty(ref _IsSelected, value); }
}
public GE_DrawingAttachViewModel()
{
LoadList();
CloseCommand = new DelegateCommand<string>(onClose);
AttachCommand = new DelegateCommand<string>(onAttach);
}
private void onAttach(string parameter)
{
ButtonResult result = new ButtonResult();
var records = DrawingList.Where(x => x.IsSelected == true).ToList();
if(records.Count>0)
{
SelectedDrawings = records;
}
//result = ButtonResult.OK;
//RaiseRequestClose(new DialogResult(result));
}
private void RaiseRequestClose(DialogResult dialogResult)
{
RequestClose?.Invoke(dialogResult);
}
private void onClose(string parameter)
{
ButtonResult result = new ButtonResult();
result = ButtonResult.Cancel;
RaiseRequestClose(new DialogResult(result));
}
private string _title="Drawing List";
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
public event Action<IDialogResult> RequestClose;
public bool CanCloseDialog()
{
return true;
}
public void OnDialogClosed()
{
}
public void OnDialogOpened(IDialogParameters parameters)
{
}
public async void LoadList()
{
GenericDataService<GE_Drawing> generic = new GenericDataService<GE_Drawing>();
DrawingList.AddRange(await generic.GetAll());
}
}
As written above I have an OnAttach which should send back my SelectedDrawings variable to the view but I dont know how to do that.
I have found the answer... IdialogResult have parameter with a value could be set to in dialog and get the value in the callback using a key.
private void openDialog()
{
DialogParameters parameter = new DialogParameters();
_dialogService.ShowDialog("GE_DrawingAttach", parameter, r =>
{
if(r.Result==ButtonResult.OK)
{
DrawingList = r.Parameters.GetValue<List<GE_Drawing>>("DrawingList");
}
});
}
Let's say there is a model:
public class MyCustomModel
{
public bool parameter { get; set; }
....
}
and there are also several ViewModels:
public class MainViewModel : INotifyPropertyChanged
{
private MyCustomModel model;
private bool _missingValue = true;
public bool MissingValue
{
get => _missingValue;
set
{
if (_missingValue == value) return;
_missingValue = value;
OnPropertyChanged(nameof(MissingValue));
}
}
public MainViewModel(MyCustomModel model)
{
this.model = model;
}
}
public class AdvancedViewModel : INotifyPropertyChanged
{
private MyCustomModel model;
private bool _missingValue = true;
public bool MissingValue
{
get => _missingValue;
set
{
if (_missingValue == value) return;
_missingValue = value;
model.parameter = _missingValue;
OnPropertyChanged(nameof(MissingValue));
}
}
public AdvancedViewModel(MyCustomModel model)
{
this.model = model;
}
}
I need to change the value in my MainViewModel when the value changes in AdvancedViewModel (need to call setter). So how to do that properly? Any suggestions? Thanks in advance.
I have a MVVM program with a model:
public class Deelnemer
{
public int Id { get; set; }
public string Voornaam { get; set; }
public string Werkplek { get; set; }
public List<string> Aanwezig { get; set; }
public Deelnemer()
{
}
}
In my View I have a listBox in which I want to be able to select multiple values (days to put in the list aanwezig).
<ListBox Name="listDagdelen" SelectionMode="Multiple" ItemsSource="{Binding Dagdelen}" SelectedItem="{Binding SelectedDagdeel, Mode=TwoWay}">
The ViewModel looks as follows:
class DeelnemerViewModel : INotifyPropertyChanged
{
#region Private Variables
private readonly Deelnemer dlnObject;
private readonly ObservableCollection<Deelnemer> deelnemers;
private readonly DeelnemerManager deelnemerManager;
private readonly ICommand addDeelnemerCmd;
private readonly ICommand deleteDeelnemerCmd;
#endregion
public ObservableCollection<string> Dagdelen { get; private set; }
#region constructor
public DeelnemerViewModel()
{
Dagdelen = new ObservableCollection<string>() { "maandagochtend", "maandagmiddag", "dinsdagochtend", "dinsdagmiddag", "woensdagochtend", "woensdagmiddag", "donderdagochtend", "donderdagmiddag", "vrijdagochtend", "vrijdagmiddag" };
dlnObject = new Deelnemer();
deelnemerManager = new DeelnemerManager();
deelnemers = new ObservableCollection<Deelnemer>();
addDeelnemerCmd = new RelayCommand(Add, CanAdd);
deleteDeelnemerCmd = new RelayCommand(Delete, CanDelete);
}
#endregion
#region Properties
private string _selectedDagdeel = null;
public string SelectedDagdeel
{
get { return _selectedDagdeel; }
set
{
_selectedDagdeel = value;
dlnObject.Aanwezig.Add(value);
OnPropertyChanged("SelectedDagdeel");
}
}
public int Id
{
get { return dlnObject.Id; }
set
{
dlnObject.Id = value;
OnPropertyChanged("Id");
}
}
public string Voornaam
{
get { return dlnObject.Voornaam; }
set
{
dlnObject.Voornaam = value;
OnPropertyChanged("Voornaam");
}
}
public string Werkplek
{
get { return dlnObject.Werkplek; }
set
{
dlnObject.Werkplek = value;
OnPropertyChanged("Werkplek");
}
}
public List<string> Aanwezig
{
get { return dlnObject.Aanwezig; }
set
{
dlnObject.Aanwezig = value;
OnPropertyChanged("Aanwezig");
}
}
public ObservableCollection<Deelnemer> Deelnemers { get { return deelnemers; } }
public Deelnemer SelectedDeelnemer
{
set
{
Id = value.Id;
Voornaam = value.Voornaam;
Werkplek = value.Werkplek;
Aanwezig = value.Aanwezig;
}
}
#endregion
#region Commands
public ICommand AddDeelnemerCmd { get { return addDeelnemerCmd; } }
public ICommand DeleteDeelnemerCmd { get { return deleteDeelnemerCmd; } }
#endregion
public bool CanAdd(object obj)
{
//Enable the Button only if the mandatory fields are filled
if (Voornaam != string.Empty && Werkplek != string.Empty)
return true;
return false;
}
public void Add(object obj)
{
var deelnemer = new Deelnemer { Voornaam = Voornaam, Werkplek = Werkplek, Aanwezig = Aanwezig };
if (deelnemerManager.Add(deelnemer))
{
Deelnemers.Add(deelnemer);
//string txt = string.Join(String.Empty,Aanwezig);
//MessageBox.Show(txt);
//ResetDeelnemer();
}
else
MessageBox.Show("Vul correcte waardes in!");
}
#region DeleteCommand
private bool CanDelete(object obj)
{
//Enable the Button only if the patients exist
if (Deelnemers.Count > 0)
return true;
return false;
}
private void Delete(object obj)
{
//Delete patient will be successfull only if the patient with this ID exists.
if (!deelnemerManager.Remove(Id))
MessageBox.Show("Deelnemer met dit id bestaat niet!");
else
{
//Remove the patient from our collection as well.
deelnemers.RemoveAt(GetIndex(Id));
ResetDeelnemer();
MessageBox.Show("Deelnemer succesvol verwijderd !");
}
}
#endregion
#region Private Methods
private void ResetDeelnemer()
{
Id = 0;
Voornaam = string.Empty;
Werkplek = string.Empty;
Aanwezig.Clear();
}
private int GetIndex(int Id)
{
for (int i = 0; i < Deelnemers.Count; i++)
if (Deelnemers[i].Id == Id)
return i;
return -1;
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
I can't figure out how I should use the List with the listbox values. How do I add (multiple) listbox values to the object's List?
The current code throws a nullreferenceexception at
dlnObject.Aanwezig.Add(value);
You must initialize the Aanwezig property of the Deelnemer object before you can add any values to it, either in the contructor of DeelnemerViewModel:
dlnObject = new Deelnemer();
dlnObject.Aanwezig = new List<string();
...or in the constructor of the Deeelnemer class:
public Deelnemer()
{
Aanwezig = new List<string();
}
I have two views : MainView and DetailView. I have a list of items to display and when user select an item and I am passing item properties to DetailViewModel and user could able to update these values.
Everything works so far, but I wonder how am I passing back to updated values to the MainViewModel ?
MainViewModel.cs
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ShowViewModel<DetailViewModel>(
new DetailViewModel.Parameter
{
Date = Date,
Age = _selectedItem.Age,
Category = _selectedItem.Category,
Discount = _selectedItem.Discount,
}
);
RaisePropertyChanged(() => SelectedItem);
}
}
DetailViewModel.cs
public class DetailViewModel: MvxViewModel
{
public double Age { get; set; }
public double Category { get; set; }
public double Discount { get; set; }
public class Parameter
{
public DateTime Date { get; set; }
public double Age { get; set; }
public int Category{ get; set; }
public double Discount { get; set; }
}
public void Init(Parameter param)
{
Age = param.Age;
Category = param.Category;
Discount = param.Discount ;
}
}
One way to pass variables between ViewModels is a Messenger based solution.
MvvmCross Messenger can be found in NuGet.
MainViewModel
private readonly IMvxMessenger _messenger;
private readonly MvxSubscriptionToken _token;
public MainViewModel(IMvxMessenger messenger) {
_messenger = messenger;
_token = messenger.Subscribe<SelectedItemMessage>(OnMessageReceived);;
}
private void OnMessageReceived(SelectedItemMessage obj)
{
SelectedItem = obj.SelectedItem;
}
DetailViewModel
private readonly IMvxMessenger _messenger;
public DetailViewModel(IMvxMessenger messenger) {
_messenger = messenger;
}
public void YourUpdateMethod() {
var message = new SelectedItemMessage(this, SelectedItem); //SelectedItem assumed it is a ViewModel property.
_messenger.Publish(message, typeof(SelectedItemMessage));
}
SelectedItemMessage
public class SelectedItemMessage : MvxMessage
{
public SelectedItemMessage(object sender, SelectedItem selectedItem) : base(sender)
{
SelectedItem = selectedItem;
}
public SelectedItem SelectedItem { get; set; }
}
Take a look at http://slodge.blogspot.nl/2013/05/n9-getting-message-n1-days-of-mvvmcross.html for a full guide to MvvmCross Messenges.
Edit using age and category in Message
public SelectedItemMessage(object sender, double age, int category) : base(sender)
{
Age = age;
Category = category;
}
public double Age { get; set; }
public int Category{ get; set; }
}
Changing the MainViewModel OnMessageReceived method
private void OnMessageReceived(SelectedItemMessage obj)
{
Age = obj.Age;
Category= obj.Category;
}
Why not just keep a reference to DetailViewModel when you create it in MainViewModel? Then any values changed in the DetailViewModel instance will be available via that reference in MainViewModel.
private DetailViewModel _detailVM;
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
_detailVM = new DetailViewModel.Parameter {
Date = Date,
Age = _selectedItem.Age,
Category = _selectedItem.Category,
Discount = _selectedItem.Discount
};
ShowViewModel<DetailViewModel>(_detailVM);
RaisePropertyChanged(() => SelectedItem);
}
}
I populate a data grid with a list of objects that come from a repository like this:
public static IEnumerable<Order> GetOrdersForDataGrid()
{
IEnumerable<Order> query;
using (RSDContext = new RSDContext())
{
query = context.Orders.Include(o=>o.OrderDetails).ToList();
}
return query;
}
When I want to edit an order I pass the selected row to a new window like this:
OrderEditWindow orderEdit = new OrderEditWindow();
orderEdit.SelectedOrder = SelectedOrder;
orderEdit.ShowDialog();
Here I set the DataContext of the Window to:
DataContext = SelectedOrder;
In this window I have another data grid that binds to OrderDetails collection property of Order. The problem is on CRUD operations on OrderDetails. For example, after I add a new orderDetail like this:
private void AddProductDetailButton_OnClick(object sender, RoutedEventArgs e)
{
if (!ValidateProductDetail())
return;
var _selectedProduct = ProductAutoCompleteBox.SelectedItem as Product;
var selectedProduct = ProductsRepository.GetProductById(_selectedProduct.ProductId);
OrderDetail orderDetail = new OrderDetail();
orderDetail.Price = selectedProduct.Price;
orderDetail.ProductCode = selectedProduct.Code;
orderDetail.ProductName = selectedProduct.Name;
orderDetail.Quantity = int.Parse(QuantityNumericUpDown.Value.ToString());
orderDetail.Um = selectedProduct.Um;
orderDetail.Total = selectedProduct.Price * int.Parse(QuantityNumericUpDown.Value.ToString());
orderDetail.Group = selectedProduct.Subgroup.Group.Name;
orderDetail.Subgroup = selectedProduct.Subgroup.Name;
orderDetail.SupplierName = selectedProduct.Supplier.Name;
//orderDetail.Order=SelectedOrder;
//orderDetail.OrderId = SelectedOrder.OrderId;
SelectedOrder.OrderDetails.Add(orderDetail);
ProductAutoCompleteBox.Text = string.Empty;
QuantityNumericUpDown.Value = 1;
ProductAutoCompleteBox.Focus();
}
and then I call the update method from repository:
public static void UpdateOrder(Order order)
{
using (RSDContext context = new RSDContext())
{
context.Orders.Attach(order);
context.Entry(order).State = EntityState.Modified;
context.SaveChanges();
}
}
I get an error about OrderId. If i set manualy the navigation property and the id I don't get an error but changes dont get saved into db.
My Order model look like this:
public class Order : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Order()
{
_OrderDetails = new ObservableCollection<OrderDetail>();
_OrderDetails.CollectionChanged += _OrderDetails_CollectionChanged;
}
void _OrderDetails_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
AttachProductChangedEventHandler(e.NewItems.Cast<OrderDetail>());
if (e.OldItems != null)
CalcualteTotals();
}
[NotMapped]
public decimal CalculatedTotal
{
get
{
return OrderDetails.Sum(x => x.Total);
}
}
public int OrderId { get; set; }
private int _Number;
public int Number
{
get { return _Number; }
set
{
_Number = value;
NotifyPropertyChanged("Number");
}
}
private DateTime _Date;
public DateTime Date
{
get { return _Date; }
set
{
_Date = value;
NotifyPropertyChanged("Date");
}
}
private bool _Canceled;
public bool Canceled
{
get { return _Canceled; }
set
{
_Canceled = value;
NotifyPropertyChanged("Canceled");
}
}
private string _ClientName;
public string ClientName
{
get { return _ClientName; }
set
{
_ClientName = value;
NotifyPropertyChanged("ClientName");
}
}
private string _ClientPhone;
public string ClientPhone
{
get { return _ClientPhone; }
set
{
_ClientPhone = value;
NotifyPropertyChanged("ClientPhone");
}
}
private string _DeliveryAddress;
public string DeliveryAddress
{
get { return _DeliveryAddress; }
set
{
_DeliveryAddress = value;
NotifyPropertyChanged("DeliveryAddress");
}
}
private decimal _Transport;
public decimal Transport
{
get { return _Transport; }
set
{
_Transport = value;
NotifyPropertyChanged("Transport");
}
}
private decimal _Total;
public decimal Total
{
get { return _Total; }
set
{
_Total = value;
NotifyPropertyChanged("Total");
}
}
private ObservableCollection<OrderDetail> _OrderDetails;
public virtual ObservableCollection<OrderDetail> OrderDetails
{
//get { return _OrderDetails ?? (_OrderDetails = new ObservableCollection<OrderDetail>()); }
get
{
return _OrderDetails;
}
set
{
_OrderDetails = value;
NotifyPropertyChanged("OrderDetails");
}
}
private void AttachProductChangedEventHandler(IEnumerable<OrderDetail> orderDetails)
{
foreach (var p in orderDetails)
{
p.PropertyChanged += (sender, e) =>
{
switch (e.PropertyName)
{
case "Quantity":
case "Price":
case "Total":
CalcualteTotals();
break;
}
};
}
CalcualteTotals();
}
public void CalcualteTotals()
{
NotifyPropertyChanged("CalculatedTotal");
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
And my OrderDetail model look like this:
public class OrderDetail : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
private int _ProductCode;
public int ProductCode
{
get { return _ProductCode; }
set
{
_ProductCode = value;
NotifyPropertyChanged("ProductCode");
}
}
private string _ProductName;
public string ProductName
{
get { return _ProductName; }
set
{
_ProductName = value;
NotifyPropertyChanged("ProductName");
}
}
private string _Um;
public string Um
{
get { return _Um; }
set
{
_Um = value;
NotifyPropertyChanged("Um");
}
}
private decimal _Price;
public decimal Price
{
get { return _Price; }
set
{
_Price = value;
NotifyPropertyChanged("Price");
NotifyPropertyChanged("Total");
}
}
private int _Quantity;
public int Quantity
{
get { return _Quantity; }
set
{
_Quantity = value;
NotifyPropertyChanged("Quantity");
NotifyPropertyChanged("Total");
}
}
private string _SupplierName;
public string SupplierName
{
get { return _SupplierName; }
set
{
_SupplierName = value;
NotifyPropertyChanged("SupplierName");
}
}
private string _Subgroup;
public string Subgroup
{
get { return _Subgroup; }
set
{
_Subgroup = value;
NotifyPropertyChanged("Subgroup");
}
}
private string _Group;
public string Group
{
get { return _Group; }
set
{
_Group = value;
NotifyPropertyChanged("Group");
}
}
public decimal _Total;
public decimal Total
{
get { return Quantity * Price; }
set
{
_Total = value;
NotifyPropertyChanged("Total");
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
I'm really trying to use some sort of unit of work and I don't understand how i'm supposed to apply CRUD on objects with child collections and keep the UI updated in the same time (by working in a ObservableCollection and using Binding ClientPhone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged my parent window is updated as I type)
A final working solution:
using (RSDContext context = new RSDContext())
{
var details = order.OrderDetails;
order.OrderDetails = null;
List<int> OriginalOrderDetailsIds =
context.OrderDetails.Where(o => o.OrderId == order.OrderId).Select(o => o.OrderDetailId).ToList();
List<int> CurrentOrderDetailsIds = details.Select(o => o.OrderDetailId).ToList();
List<int> DeletedOrderDetailsIds = OriginalOrderDetailsIds.Except(CurrentOrderDetailsIds).ToList();
context.Entry(order).State = EntityState.Modified;
foreach (var deletedOrderDetailId in DeletedOrderDetailsIds)
{
context.Entry(context.OrderDetails.Single(o => o.OrderDetailId == deletedOrderDetailId)).State = EntityState.Deleted;
}
foreach (OrderDetail detail in details)
{
// Add.
if (detail.OrderDetailId == 0)
{
detail.OrderId = order.OrderId;
context.Entry(detail).State = EntityState.Added;
}
// Update.
else
{
context.Entry(detail).State = EntityState.Modified;
}
}
context.SaveChanges();
}
You could do this way for adding and updating the child, but not sure about deleted order details in the ui. If you don't want to get the order from entity, you need some kind of marking in the OrderDetail for deleted OrderDetail.
using (RSDContext context = new RSDContext())
{
var details = order.OrderDetails;
order.OrderDetails = null;
context.Entry(order).State = EntityState.Modified;
foreach (var detail in details)
{
if (detail.Id == 0)
{
// Adds.
detail.OrderId = order.Id;
context.Entry(detail).State = EntityState.Added;
}
else if (detail.IsDeleted)
// Adds new property called 'IsDeleted'
// and add [NotMapped] attribute
// then mark this property as true from the UI for deleted items.
{
// Deletes.
context.Entry(detail).State = EntityState.Deleted;
}
else
{
// Updates.
context.Entry(detail).State = EntityState.Modified;
}
}
order.OrderDetails = details;
context.SaveChanges();
}