I cannot get updated item in ListView after modifying existing database item. Though, once I reload the application one can see updated item in ListView.
I have binded to an ObservableCollection for the ListView
This is my interface
public interface IService
{
IEnumerable<Employee> GetDetails();
IEnumerable<Employee> GetDetailsById(int MatchID);
}
I have implemented IService IEmployeeServiceData class.
public class IEmployeeServiceData:IService
{
private EmployeeContext Context
{
get;
set;
}
public IEmployeeServiceData()
{
Context = new EmployeeContext();
}
public IEnumerable<Model.Employee> GetDetails()
{
return Context.Employees;
}
public IEnumerable<Model.Employee> GetDetailsById(int MatchID)
{
var q = from f in Context.Employees
where f.EMPLOYEE_ID == MatchID
select f;
return q.AsEnumerable();
}
}
This is my VM
public void RefereshData()
{
var e = EmployeeService.GetDetails();
if (SelectedIndexValue == 1)
{
var Data = from e1 in e
where e1.LOCATION == "Pune"
select e1;
EmployeeMasterData = new ObservableCollection<Model.Employee>(Data);
}
else if(SelectedIndexValue==2)
{
var Data = from e1 in e
where e1.LOCATION == "Bangalore"
select e1;
EmployeeMasterData = new ObservableCollection<Model.Employee>(Data);
}
else
{
EmployeeMasterData = new ObservableCollection<Model.Employee>(e);
}
}
Updating Exiting Item:
public void UpdateEmployee()
{
try
{
Context = new EmployeeContext();
Employee Emp = Context.Employees.First(i => i.EMPLOYEE_ID == FindId1);
Emp.FIRST_NAME = this.FIRSTNAME;
Emp.FAMILY_NAME = this.FAMILY_NAME;
Emp.EXTERNAL_ID = this.EXTERNALID;
Emp.DB_SPONSOR = this.DBSPONSOR;
Emp.LEGAL_ENTITY = this.LEGAL_ENTITY;
Emp.COST_CENTER = this.COST_CENTER1;
Emp.STATUS = this.StatusCategory;
Emp.ENTRY_DATE = this.ENTRYDATE;
Emp.LOCATION = this.LocationCategory1;
Context.SaveChanges();
Clear();
AlertMessage1 = "Employee Record is Updated Sucessfulyy !!!";
IsVisible1 = true;
timer.Start();
}
catch(Exception ex)
{
Console.WriteLine(ex.InnerException);
}
}
Existing Item
Updated Item
Changes done to an entity in Entity Framework will not be reflected on screen because the two instances are not related in your example. Yes the have the same values, but they are two different distinct reference locations in memory. **For the ObservableCollection is a copy of the list and not the actual list being manipulated in your example.
Hence they are not related.
To show a change you have these options:
Change the actual object's property(ies) held by the observable collection to mirror the change done in to the other EF entity. Also the EF entity must adhere to INotifyPropertyChange or also the data property change won't be seen on the screen.
Or delete the screen entity and add the changed entity into the list.
Or delete the whole observable list and re-add it with the latest version from EF. (You mention that you do this and that is an option as well).
Related
I've implemented deep cloning of ObservableCollection in order to reset items to It's original state in editable Datagrid, via cancel button.
For this I have two collections - one ObservableCollection to bind Datagrid to It, and cloned List to re-initialize ObservableCollection to It's original state when needed.
My code works only first time I hit a cancel button, after that my cloned List has changes in It too.
Provided code is an example (mine is a bit longer), but It's 100% same as mine:
Model, which implements ICloneable:
public class EmployeeModel : ICloneable
{
public object Clone()
{
return MemberwiseClone();
}
public string NAME
{
get { return _name; }
set
{
if (_name != value)
{
CHANGE = true;
_name = value;
}
}
}
private string _name;
public string SURNAME
{
get { return _surname; }
set
{
if (_surname != value)
{
CHANGE = true;
_surname = value;
}
}
}
private string _surname;
///<summary>Property for tracking changes in model</summary>
public bool CHANGE { get; set; }
}
Viewmodel:
public ViewModel() : Base //Implements InotifyPropertyChanged
{
public ViewModel()
{
Task.Run(()=> GetData());
}
public ObservableCollection<EmployeeModel> Employees
{
get { return _employees; }
set { _employees = value; OnPropertyChanged();}
}
private ObservableCollection<EmployeeModel> _employees;
public List<EmployeeModel> Copy_employees
{
get { return _copy_employees; }
set { _copy_employees = value; OnPropertyChanged();}
}
private List<EmployeeModel> _copy_employees;
//Fetch data from DB
private async Task Get_data()
{
//Returns new ObservableCollection of type Employee
Employees = await _procedures.Get_employees();
if (Employees != null) //Now make a deep copy of Collection
{
Copy_employees = new List<EmployeeModel>();
Copy_employees = Employees.Select(s => (EmployeeModel)s.Clone()).ToList();
}
}
//My Command for canceling changes (reseting DataGrid)
//CanExecute happens, when model is changed - tracking via CHANGE property of EmployeeModel
public void Cancel_Execute(object parameter)
{
Employees.Clear(); //Tried with re-initializing too, but same result
foreach (var item in Copy_employees)// Reset binded ObservableCollection with old items
{
Employees.Add(item);
}
//Check if copied List really hasn't got any changes
foreach (EmployeeModel item in Copy_employees)
{
Console.WriteLine("Changes are " + item.CHANGES.ToString());
}
}
}
Output of cancel command:
1.) First time I hit cancel button:
// Changes are False
Every next time:
// Changes are True
So, as I see It from Console, my copied List get's updated when ObservableColection get's updated, even if It's not binded to DataGrid.
And It updates only a property which I changed, so List reflects ObservableCollection items.
How can I keep my original items of List<Employee>, and copy those into binded ObservableCollection anytime ?
When you return values, you do not return them, but write backing item references to the editable collection.
As a result, you have the same instances in both collections.
In the simplest case, when you return them, you also need to clone.
public void Cancel_Execute(object parameter)
{
Employees.Clear(); //Tried with re-initializing too, but same result
foreach (var item in Copy_employees)// Reset binded ObservableCollection with old items
{
Employees.Add((EmployeeModel)item.Clone());
}
//Check if copied List really hasn't got any changes
foreach (EmployeeModel item in Copy_employees)
{
Console.WriteLine("Changes are " + item.CHANGES.ToString());
}
}
Not relevant to the question, but I still advise you to use a slightly more user-friendly interface for cloneable:
public interface ICloneable<T> : ICloneable
{
new T Clone();
}
I have Live Chart that I am trying to perform a fresh of the values on Button_Click event but the Chart is not refreshing.
I have two TextBoxes where the user can select the start and end date they would like to review and use the butn_ExecuteQuery_Click to display the data.
public HBDBreakdown()
{
InitializeComponent();
ChartValues();
}
private void ChartValues()
{
try
{
// Defines the variable for differnt lines.
List<double> SmallCommercialIndustValues = new List<double>();
List<double> ResidentialValues = new List<double>();
List<string> AnalystName = new List<string>();
SqlConnection connection = new SqlConnection("Data Source=WINDOWS-B1AT5HC\\MSSQLSERVER2;Initial Catalog=CustomerRelations;user id=sa; password=Westside2$; Integrated Security=False;");
string selectQuery = ("SELECT Users.TX_EMPLOYEE, SUM(CASE WHEN d .REV_CLS <> 2 THEN 1 ELSE 0 END) AS Residential, SUM(CASE WHEN d .REV_CLS = 2 THEN 1 ELSE 0 END) AS SmallCommercialIndust FROM hb_Disputes AS d INNER JOIN Users ON d.ASSGNTO = Users.KY_USER_ID WHERE(d.OPENED >=#OPENED) AND(d.OPENED < #CLOSED) GROUP BY Users.TX_EMPLOYEE; ");
connection.Open();
SqlCommand command = new SqlCommand(selectQuery, connection);
command.Parameters.Add("#OPENED", SqlDbType.DateTime).Value = dtepicker_Open.Text;
command.Parameters.Add("#CLOSED", SqlDbType.DateTime).Value = dtepicker_DateResolved.Text;
SqlDataReader sqlReader = command.ExecuteReader();
while (sqlReader.Read())
{
// Check for DBNull and then assign the variable
if (sqlReader["SmallCommercialIndust"] != DBNull.Value)
SmallCommercialIndustValues.Add(Convert.ToInt32(sqlReader["SmallCommercialIndust"]));
// Check for DBNull and then assign the variable
if (sqlReader["Residential"] != DBNull.Value)
ResidentialValues.Add(Convert.ToInt32(sqlReader["Residential"]));
// Check for DBNull and then assign the variable
AnalystName.Add(Convert.ToString(sqlReader["TX_EMPLOYEE"]));
}
SeriesCollection = new SeriesCollection
{
new StackedColumnSeries
{
Title = "Residential",
Values = new ChartValues<double>(ResidentialValues),
StackMode = StackMode.Values, // this is not necessary, values is the default stack mode
DataLabels = true
},
new StackedColumnSeries
{
Title = "Small Commercial Indust",
Values = new ChartValues<double>(SmallCommercialIndustValues),
StackMode = StackMode.Values,
DataLabels = true
}
};
Labels = AnalystName.ToArray();
//Formatter = value => value + " Disputes";
DataContext = this;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public SeriesCollection SeriesCollection { get; set; }
public string[] Labels { get; set; }
public Func<double, string> Formatter { get; set; }
private void butn_ExecuteQuery_Click(object sender, RoutedEventArgs e)
{
// Refresh Chart
ChartValues();
}
Set DataContext = null; before you call ChartValues() or implement INotifyPropertyChanged and raise the PropertyChanged event from the setters of the source properties.
Better never reset the DataContext only to update the data binding of certain properties. This results in very bad performance as the complete view will be forced to render again. The DataContext is also inherited down the element tree.
Better write cleaner code and handle dynamic data more gracefully and as intended by the framework (see Data Binding Overview).
Inside a control (or on a DependencyObject in general) you should always implement all properties that are involved in data binding as DependencyProperty.
They will automatically refresh the target and depending on the Binding.Mode also the source of the specific Binding.
Since you are reassigning SeriesCollection and Labels, both properties should be a DependencyProperty.
In your case, you can go without implementing dependency properties, because you are only updating collections. So simply avoid reassigning a new collection on each refresh and instead use IList.Clear and IList.Add on collections that implement INotifyCollectionChanged.
SeriesCollection already implements INotifyCollectionChanged, so you only need to change the type of the Labels property from string[] to ObservableCollection<string>. This way IList.Clear and IList.Add operations will be automatically reflected by the binding target (the chart control):
public SeriesCollection SeriesCollection { get; set; }
public ObservableCollection<string> Labels { get; set; }
public HBDBreakdown()
{
InitializeComponent();
// Set the `DataContext` once in the constructor
this.DataContext = this;
// Initialize the collection to prepare them for dynamic clear/add
this.SeriesCollection = new SeriesCollection();
this.Labels = new ObservableCollection<string>();
}
private void ChartValues()
{
try
{
...
this.SeriesCollection.Clear();
this.SeriesCollection.Add(
new StackedColumnSeries
{
Title = "Residential",
Values = new ChartValues<double>(ResidentialValues),
StackMode = StackMode.Values, // this is not necessary, values is the default stack mode
DataLabels = true
});
this.SeriesCollection.Add(
new StackedColumnSeries
{
Title = "Small Commercial Indust",
Values = new ChartValues<double>(SmallCommercialIndustValues),
StackMode = StackMode.Values,
DataLabels = true
});
this.Labels.Clear();
AnalystName.ForEach(this.Labels.Add);
}
// Code smell: never catch 'Exception'.
// Only catch explicitly those exception you can handle.
// A user can't handle exceptions, so in a real business application you wouldn't show the exception message to the user.
catch (Exception ex)
{
// Bad practice
MessageBox.Show(ex.Message);
}
}
Please also read: Exceptions and Exception Handling, especially Exceptions Overview and Design Guidelines for Exceptions.
I need a TreeView to represent some hierarchical data from multiple tables stored in a SQL Server CE database. Before, the data was stored in xml and was simple deserialized on startup and everything was good. Now I was asked to move data to a database and I've faced a several problems.
My first problem was that it takes quite a long time to retrieve many items from DB and build a TreeView ViewModel from this items (still not sure what is longer - to get items or to construct this tree). So I implemented lazy loading and now I'm getting items only when a TreeViewItem is expanding.
Now, I need to perform a text search over all the nodes, but to make it work, all nodes must be loaded.
I tried to load all of them but the UI freezes while the tree is loading. Doing this inside a BackgroundWorker is also impossible for me because the items are stored in an ObservableCollection and I'm getting "InvalidOperationException". Using Dispatcher helps with this but it is also freezes UI...
The excerpt from my TreeViewItem VM is below, if more code is needed please let me know. Maybe I am totally wrong with my design, so any comments are very appreciated!
public class TreeViewItemViewModel: DisplayableItem, IItemsHost
{
internal static DummyTreeViewItemViewModel _dummy = new DummyTreeViewItemViewModel();
public TreeViewItemViewModel(){}
public TreeViewItemViewModel(IDisplayableItem displayableItem)
{
Data = displayableItem;
}
public TreeViewItemViewModel(IDisplayableItem displayableItem, IDisplayableItem parent)
:this(displayableItem)
{
Parent = parent as TreeViewItemViewModel;
}
private TreeViewItemViewModel _parent;
public TreeViewItemViewModel Parent
{
get { return _parent; }
set { _parent = value; InvokePropertyChanged(new PropertyChangedEventArgs("Parent")); }
}
private IDisplayableItem _data;
public new IDisplayableItem Data
{
get { return _data; }
set { _data = value; InvokePropertyChanged(new PropertyChangedEventArgs("Data")); }
}
private bool _isSelected;
public new bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; InvokePropertyChanged(new PropertyChangedEventArgs("IsSelected")); }
}
private bool _isEnabled=true;
public new bool IsEnabled
{
get { return _isEnabled; }
set { _isEnabled = value; InvokePropertyChanged(new PropertyChangedEventArgs("IsEnabled")); }
}
private bool _isVisible = true;
public new bool IsVisible
{
get { return _isVisible; }
set { _isVisible = value; InvokePropertyChanged(new PropertyChangedEventArgs("IsVisible")); }
}
private void FillItems()
{
if (Items.Contains(_dummy))
{
Items.Remove(_dummy);
var itemshost = Data as IItemsHost;
if (itemshost != null)
{
_items = new ObservableCollection<IDisplayableItem>();
foreach (var item in itemshost.Items)//getting 'Items' actually requesting them from a database
{
var treeItem = new TreeViewItemViewModel(item, this);
_items.Add(treeItem);
}
InvokePropertyChanged(new PropertyChangedEventArgs("Items"));
}
}
}
protected bool _isExpanded;
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if(value)
{
FillItems();
}
_isExpanded = value;
InvokePropertyChanged(new PropertyChangedEventArgs("IsExpanded"));
}
}
protected SObservableCollection<IDisplayableItem> _items = new SObservableCollection<IDisplayableItem>();
public SObservableCollection<IDisplayableItem> Items
{
get
{
var itemshost = Data as IItemsHost;
if (itemshost != null)
{
if (_items.Count == 0 && itemshost.Items.Count > 0)
_items.Add(_dummy);
}
return _items;
}
set { _items = value; InvokePropertyChanged(new PropertyChangedEventArgs("Items")); }
}
UPDATE: for those who would search for a similar solution - my problem was in my query method. I shouldn't open a new SQL Server CE connection each time I need to perform a query...
What about a new DB table that holds a flattened representation of the entire hierarchy, and have your search logic query this table? You'll obviously need to keep this table updated as you insert/update/delete records in the other tables.
Each record in the new table would need to include some information about where the item sits in the hierarchy, so that when you get the search results back you can load and populate just those tree nodes containing the "hits".
Since reading from database is being done asynchronously so the performance bottle-neck should be constructing View from ViewModel. I suggest the following method:
Read all essential Model data from database in one async call and store them in an object called SearchHelper.
Add a a simple property (Model.Id or Model's hash code) to every ViewModel that you create in order to find the equivalent view model of an specific model.
Create only visible ViewModels. (lazy loading for ViewModel only)
Use the SearchHelper to find matches for the search query, then using the Id or hash code of the results, you can easily locate their equivalent view models.
Please Consider:
Once loaded, SearchHelper does not update itself, so you might want to manually update it.
For this method to have optimal performance, try avoiding iteration of all nodes. instead, store the sequence of traced items (their index or Id) in order to find them in view model. if each Model item knows its parent, then the back-tracking should be easy.
Is it possible to reserve an IDENTITY value for a to-be-added row in the database using Entity Framework?
In my override of ObjectContext.SaveChanges, I want to do something like this:
public override int SaveChanges(SaveOptions options)
{
var affectedEntities = ObjectStateManager.GetObjectStateEntries(
EntityState.Added |
EntityState.Modified |
EntityState.Deleted);
foreach (var entry in affectedEntities)
{
var type = GetObjectType(entry.Entity.GetType());
if (entry.State == EntityState.Added)
{
// log the creation of the new entity into a log file
// or a different table. For this, I need the newly
// created entity's ID. Is it possible?
}
}
return base.SaveChanges(options);
// if I check for ObjectState here, i.e. after SaveChanges,
// it won't work? Because once the changes are committed to the database
// the newly added object's state would no longer remain
// Added.
}
You can't ask the database to preallocate the id's. You have 2 options
implement your own identity server
get the id's after the data has been written to the database
Here's a couple of examples of option 2. They both do the same thing - build a list of added items before the call to SaveChanges() and iterate over them after.
public interface IIDentifiable
{
int id { get; set; }
}
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
List<IIDentifiable> addedObjects = GetNewEntries();
int result = base.SaveChanges();
foreach (IIDentifiable o in addedObjects)
{
//log o.id
}
return result;
}
private List<IIDentifiable> GetNewEntries()
{
var result = (
from e in ChangeTracker.Entries()
where e.State == System.Data.EntityState.Added
select e.Entity)
.OfType<IIDentifiable>()
.ToList();
return result;
}
the drawback of IIDentifiable is getting other details such as Type etc.
The second option is to do the same thing but use dynamic instead of IIDentifiable
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
List<dynamic> addedObjects = GetNewEntries();
int result = base.SaveChanges();
foreach (dynamic o in addedObjects)
{
//log o.id
//log o.GetType()
}
return result;
}
private List<dynamic> GetNewEntries()
{
var result = (
from e in ChangeTracker.Entries()
where e.State == System.Data.EntityState.Added
select e.Entity)
.ToList();
return result;
}
I have the following code (simplified for brevity)
[ActiveRecord(Table = "[Order]"),
Serializable]
public class Order : ActiveRecordLinqBase<Order>, INotifyPropertyChanged
{
//Other properties omitted for brevity
private IList<Reading> _readings;
[HasMany(Inverse = true, Cascade = ManyRelationCascadeEnum.All)]
public virtual IList<Reading> Readings
{
get { return _readings; }
set
{
_readings = value;
DoPropertyChanged(() => this.Readings);
}
}
}
using (new SessionScope(FlushAction.Never) {
Order o = _repository.GetOrder(OrderId);
bool result = ShowReadings(this, o);
if (result) s.Flush();
}
bool ShowReadings(Window owner, Order o) {
//populate a datagrid with Reading objects from Order
this.Readings = Order.Readings //datagrid binds to Readings property of the form
if (cbxHistoricalChecked) {
using(new DifferentDatabaseScope(SomeOtherConnectionString)) {
//bind datagrid to historical readings
this.Readings = GetHistoricalReadings(Order.OrderId);
}
}
if (SignedOff) {
using(var t = new TransactionScope()) {
//update some properties on the Order
t.VoteCommit();
}
}
return true;
}
So what I'm doing is having a SessionScope open while the readings form is open, and flushed once it is closed. If the user wants to view historical readings for the order, I have to open a different database for the historical readings.
Things work fine as long as I don't open the DifferentDatabaseScope, but when I do, the creation of a new TransactionScope fails:
Castle.ActiveRecord.Framework.Scopes.ScopeMachineryException {"Tried to unregister a scope that is not the active one"}
Is it possible to do what I'm trying to do, and if so what is the proper way to do it?