I'm using local database to store my data in Windows Phone 8 application. At first I have data stored in JSON object which is converted to my classes objects and then collection of these objects I try to store in local database. I was checking in debug mode and data is in those objects, but when I check database, it's empty.
This is how I move data from collection to database:
// Data context for the local database
private TablesDataContext tablesDB;
// Define the query to gather all of items.
var customersTablesInDB = from CustomerItem todo in tablesDB.CustomersTable
select todo;
// Execute the query and place the results into a collection.
CustomersTable = new ObservableCollection<CustomerItem>(customersTablesInDB);
foreach (Customer customer in customersList)
{
// Create a new item
CustomerItem newCustomer = new CustomerItem
{
Id = customer.id,
Number = customer.number.Value,
Name = customer.name,
Email = customer.email
};
// Add item to the observable collection.
CustomersTable.Add(newCustomer);
// Add item to the local database.
tablesDB.CustomersTable.InsertOnSubmit(newCustomer);
}
Here is my class for DataContext:
public class TablesDataContext : DataContext
{
// Specify the connection string as a static, used in main page and app.xaml.
public static string DBConnectionString = "Data Source=isostore:/Customers.sdf";
// Pass the connection string to the base class.
public TablesDataContext(string connectionString)
: base(connectionString)
{ }
// Specify a single table for the items.
public Table<CustomerItem> CustomersTable;
}
And here is my CustomerItem class:
[Table]
public class CustomerItem : INotifyPropertyChanged, INotifyPropertyChanging
{
// Define ID: private field, public property and database column.
private int _id;
[Column(IsPrimaryKey = true, IsDbGenerated = false, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int Id
{
get
{
return _id;
}
set
{
if (_id != value)
{
NotifyPropertyChanging("Id");
_id = value;
NotifyPropertyChanged("Id");
}
}
}
// Define item name: private field, public property and database column.
private int? _number;
[Column]
public int? Number
{
get
{
return _number;
}
set
{
if (_number != value)
{
NotifyPropertyChanging("Number");
_number = value;
NotifyPropertyChanged("Number");
}
}
}
// Define completion value: private field, public property and database column.
private String _name;
[Column]
public String Name
{
get
{
return _name;
}
set
{
if (_name != value)
{
NotifyPropertyChanging("Name");
name = value;
NotifyPropertyChanged("Name");
}
}
}
// Define completion value: private field, public property and database column.
private String _email;
[Column]
public String Email
{
get
{
return _email;
}
set
{
if (_email != value)
{
NotifyPropertyChanging("Email");
_email = value;
NotifyPropertyChanged("Email");
}
}
}
// Version column aids update performance.
[Column(IsVersion = true)]
private Binary _version;
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify the page that a data context property changed
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
// Used to notify the data context that a data context property is about to change
private void NotifyPropertyChanging(String propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}
You are missing a "tablesDB.SubmitChanges()" after your foreach-loop.
Related
I have an observable collection of Suppliers that I want to load into a gridview and then have users edit any relevant information on the supplier. My issue is I'm not sure how to implement an IsDirty field for each property on the supplier (Model) that can be changed. I have the IsDirty bits created as such
#region SupplierID
private int _SupplierID;
public int SupplierID
{
get
{
return _SupplierID;
}
set
{
if (_SupplierID != value)
{
_SupplierID = value;
OnPropertyChanged("SupplierID");
}
}
}
#endregion
#region Address
private string _Address;
public string Address
{
get
{
return _Address;
}
set
{
if (_Address != value)
{
_Address = value;
IsDirtyAddress = true;
OnPropertyChanged("Address");
}
}
}
public bool IsDirtyAddress{ get; set; }
#endregion
#region City
private string _City;
public string City
{
get
{
return _City;
}
set
{
if (_City != value)
{
_City = value;
IsDirtyCity = true;
OnPropertyChanged("City");
}
}
}
public bool IsDirtyCity { get; set; }
#endregion
#region State
private string _State;
public string State
{
get
{
return _State;
}
set
{
if (_State != value)
{
_State = value;
IsDirtyState = true;
OnPropertyChanged("State");
}
}
}
public bool IsDirtyState { get; set; }
#endregion
#region Zip
private string _Zip;
public string Zip
{
get
{
return _Zip;
}
set
{
if (_Zip != value)
{
_Zip = value;
IsDirtyZip = true;
OnPropertyChanged("Zip");
}
}
}
public bool IsDirtyZip { get; set; }
#endregion
The problem is that when I build the list of suppliers (ViewModel), I actually end up setting the IsDirty bits to true. What is the best way to set my Address, City, State, Zip when creating the supplier without setting the IsDirty bits to true. Do I need an initialization function in my Model?
for (int i = 0; i < dtSupplier.Rows.Count; i++)
{
Supplier s = new Supplier()
{
SupplierID = Convert.ToInt32(dtSupplier.Rows[i]["SupplierID"].ToString()),
Address = dtSupplier.Rows[i]["Address"].ToString(),
City = dtSupplier.Rows[i]["City"].ToString(),
State = dtSupplier.Rows[i]["State"].ToString(),
Zip = dtSupplier.Rows[i]["Zip"].ToString()
};
Suppliers.Add(s);
}
Maybe I'm going about the whole IsDirty approach the wrong way. I just want to know which values actually changed so my SQL update statement will only include the changed values when a user saves. Thanks!
You need to do a few things:
Add a flag to your ViewModel and name it Loading. When you are loading the ViewModel, set the Loading property to true. When finished loading, set it to false.
Pass your model to your ViewModel but do not expose it. Simply store it in your ViewModel.
When the property is set, check if the ViewModel is in state Loading and do not set IsDirty flags. Also, even if not in loading state, compare the values to the value in your model and see if they are the same.
Do not use hardcoded strings because it is easy to make a mistake. Use nameof (see my example below).
Do not let other people from outside set the IsDirty flag so make the setter private.
I am pretty sure there are libraries that do this already but I do not know of any so perhaps someone else can chime in.
Here is a hypothetical example:
public class Model
{
public string Name { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
private readonly Model model;
public ViewModel(Model model)
{
this.model = model;
}
public bool Loading { get; set; }
public bool IsDirtyName { get; private set; }
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if (this.Loading)
{
this.name = value;
return;
}
if (this.model.Name != value)
{
IsDirtyName = true;
OnPropertyChanged(nameof(Name));
}
}
}
private void OnPropertyChanged(string propertyName)
{
// ...
}
public event PropertyChangedEventHandler PropertyChanged;
}
If you pay attention the above design, you do not even need all those IsDirty flags and IsLoading etc. You can actually just have one method in your ViewModel that you call during saving and ask it to check and return all the properties that have changed. The ViewModel will compare its own properties against the Model properties and return a dictionary. There are many ways do achieve what you want.
One option is to handle the IsDirty logic on a different class which will store the original values of the Supplier object instance. You can then use that class to GetChangedPropertyNames or check if your object HasChanges.
class Supplier
{
private string _Address;
public string Address
{
get
{
return _Address;
}
set
{
if (_Address != value)
{
_Address = value;
OnPropertyChanged("Address");
}
}
}
}
class SupplierIsDirtyTracker
{
private Dictionary<string, object> _originalPropertyValues = new Dictionary<string, object>();
private Supplier _supplier;
public void Track(Supplier supplier)
{
_supplier = supplier;
_originalPropertyValues.Add(nameof(Supplier.Address), supplier.Address);
}
public bool HasChanges()
{
return !Equals(_originalPropertyValues[nameof(Supplier.Address)], _supplier.Address);
}
public IEnumerable<string> GetChangedPropertyNames()
{
if(!Equals(_originalPropertyValues[nameof(Supplier.Address)], _supplier.Address))
{
yield return nameof(Supplier.Address);
}
}
}
You can also use Reflection on your IsDirtyTracker class to eliminate hardcoding the property names.
I have class in xamarin form app and I implemented INotifyChanged on it. Later this class will be used to create a list. The data for the list comes from mysql. The list will be the item source for my third party dataGrid called sfdataGrid. The column actual reading will display integer from db and can be edited in the data grid. But the newly edited value must be bigger than the value initial value from db. Or it should revert back to initial value. How can compare the new value with initial value property Changed?
public class actualmeterreading : INotifyPropertyChanged
{
private string _ID;
private string _MachineMeterReadingID;
private Int32 _ActualReading;
private machinemeterreadinglist _MachineMeterReadingList;
public actualmeterreading(string id, string machinemeterreadingid, Int32 actualreading, machinemeterreadinglist machinemeterreadinglist)
{
this._ID = id;
this._MachineMeterReadingID = machinemeterreadingid;
this._ActualReading = actualreading;
this._MachineMeterReadingList = machinemeterreadinglist;
}
public actualmeterreading()
{
this._ID = string.Empty;
this._MachineMeterReadingID = string.Empty;
this._ActualReading = 0;
this._MachineMeterReadingList = new machinemeterreadinglist();
}
public string ID
{
get { return _ID;}
set { _ID = value;}
}
public string MachineMeterReadingID
{
get { return _MachineMeterReadingID;}
set { _MachineMeterReadingID = value;}
}
public int ActualReading
{
get { return _ActualReading;}
set {
_ActualReading = value;
RaisePropertyChanged("ActualReading");
}
}
public machinemeterreadinglist MachineMeterReadingList
{
get { return _MachineMeterReadingList;}
set { _MachineMeterReadingList = value;}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(String Name)
{
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(Name));
}
}
public int ActualReading
{
get { return _ActualReading;}
set {
// only update if new value is bigger than old value
if (value > _ActualReading) {
_ActualReading = value;
RaisePropertyChanged("ActualReading");
}
}
}
I have created local database in windows phone 8 app. I have 4 fields.
userID - int
Username - string
FileName - string
FileByte - byte[]
What I am doing is trying to update the FileByte column. But when I update the column I get exception SQL Server does not handle comparison of NText, Text, Xml, or Image data types.
Here is my DataTable
[Table]
public class UserFilesDetailsTable : INotifyPropertyChanged, INotifyPropertyChanging
{
// Define ID: private field, public property, and database column.
private int _userID;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int userID
{
get { return _userID; }
set
{
if (_userID != value)
{
NotifyPropertyChanging("userID");
_userID = value;
NotifyPropertyChanged("userID");
}
}
}
// Define item name: private field, public property, and database column.
private string _Username;
[Column(DbType = "NVarChar(100) NOT NULL", CanBeNull = false)]
public string Username
{
get { return _Username; }
set
{
if (_Username != value)
{
NotifyPropertyChanging("Username");
_Username = value;
NotifyPropertyChanged("Username");
}
}
}
// Define item name: private field, public property, and database column.
private string _Filename;
[Column(DbType = "NVarChar(100) NOT NULL", CanBeNull = false)]
public string Filename
{
get { return _Filename; }
set
{
if (_Filename != value)
{
NotifyPropertyChanging("Filename");
_Filename = value;
NotifyPropertyChanged("Filename");
}
}
}
// Define item name: private field, public property, and database column.
private byte[] _Filebytes;
[Column(DbType = "image")]
public byte[] Filebytes
{
get { return _Filebytes; }
set
{
if (_Filebytes != value)
{
NotifyPropertyChanging("Filebytes");
_Filebytes = value;
NotifyPropertyChanged("Filebytes");
}
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify that a property changed
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
// Used to notify that a property is about to change
private void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}
Here my Update query
public void addFiles(int userID, string userName, string fileName, byte[] fileBytes)
{
try
{
if (!(databaseTablesDB.usersFileDetailsTable.Where(f => f.Filename == fileName).Any()))
{
databaseTablesDB.usersFileDetailsTable.InsertOnSubmit(new UserFilesDetailsTable { userID = userID, Username = userName, Filename = fileName, Filebytes = fileBytes });
// Save changes to the database.
databaseTablesDB.SubmitChanges();
}
else
{
var fileDetails = (from file in databaseTablesDB.usersFileDetailsTable where file.Filename == fileName && file.Username == userName select file).FirstOrDefault();
if (fileDetails != null)
{
fileDetails.Filebytes = fileBytes;
}
databaseTablesDB.SubmitChanges();
}
}
catch (Exception ex)
{
}
}
I am not getting where is the issue. Can some one please help to solve this?
Take a look at SQL Server does not handle comparison of NText, Text, Xml, or Image data types. It suggests you change the type of your FileByte column to VARBINARY(MAX).
I have a Linq DataContext as a database for the application. I have set up the MVVM pattern and am able to insert new records into the database. However when I load these records and try update them a new instance of the record is being created in the background and being updated with the property changes. So when they UI is invoking the save command the originally loaded instance of the record has no changes and is not saved.
from what I can tell this is the sequence of events
Load Instance 1
Start updating property
NotifyPropertyChanging is called
New instance2 is loaded
New Instance2 is updated
Invoke save changes from UI for Instance 1
No changes are made because Instance 1 has not been updated
Below is the code I have:
/* This is the Entity */
[Table]
public class User : IDisposable, INotifyPropertyChanged, INotifyPropertyChanging
{
private MyDataContext context;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangingEventHandler PropertyChanging;
private void NotifyPropertyChanging(String propertyName)
{
PropertyChangingEventHandler handler = PropertyChanging;
if (null != handler)
{
handler(this, new PropertyChangingEventArgs(propertyName));
}
}
public void Dispose()
{
context.Dispose();
}
private Guid _id;
[Column(IsPrimaryKey = true, IsDbGenerated = false, DbType = "UNIQUEIDENTIFIER NOT NULL", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public Guid Id
{
get { return _id; }
set
{
if (_id != value)
{
NotifyPropertyChanging("Id");
_id = value;
NotifyPropertyChanged("Id");
}
}
}
private string _name;
[Column(CanBeNull = false)]
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
NotifyPropertyChanging("Name"); // This line creates the new entity
_name = value;
NotifyPropertyChanged("Name");
}
}
}
public User()
{
this.context = MyDataContext.GetContext();
}
public override void SaveChanges()
{
if (_id == Guid.Empty)
{
this.Id = Guid.NewGuid();
context.Users.InsertOnSubmit(this);
context.SubmitChanges();
}
else
{
context.SubmitChanges();
}
}
public static User NewInstance()
{
return new User
{
Name = String.Empty
};
}
}
/* This is the data context */
public class MyDataContext : DataContext
{
// Specify the connection string as a static, used in main page and app.xaml.
public static string ConnectionString = "Data Source=isostore:/MyApp.sdf;Password=pwd";
public MyDataContext(string connectionString) : base(connectionString) { }
public static MyDataContext GetContext()
{
var context = new MyDataContext(ConnectionString);
return context;
}
public Table<User> Users;
}
/* This is the View Model */
public sealed class UserViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private User _user;
public UserViewModel(Guid id)
{
using (MyDataContext context = new MyDataContext(MyDataContext.ConnectionString))
{
_User = context.User.First(u => u.Id == id);
}
}
public UserViewModel(User user)
{
_user = user;
}
public UserViewModel()
{
_user = User.NewInstance();
}
public string Name
{
get { return _user.Name; }
set
{
_user.Name = value;
NotifyPropertyChanged("Name");
}
}
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
return _saveCommand ?? (_saveCommand = new GenericCommand(() =>
{
_user.SaveChanges();
}, true));
}
}
}
In your MyDataContext I would think you want the below instead, a basic singleton concept so that you're working on the same object and thus saving the same object with changes.
private static DataContext context = null;
public static MyDataContext GetContext()
{
if(context == null)
context = new MyDataContext(ConnectionString);
return context;
}
edit- Note, this can have a major impact on your application in the big picture. May need to redesign when a new one is created, and if/when you should specifically set it to null.
In project i user SQL CE, i have Table:
[Table]
public class Article : INotifyPropertyChanged, INotifyPropertyChanging
{
// Define _cid: private field, public property, and database column.
private int _aid;
[Column(DbType = "INT NOT NULL IDENTITY", IsDbGenerated = true, IsPrimaryKey = true)]
public int aid
{
get { return _aid; }
set
{
NotifyPropertyChanging("aid");
_aid = value;
NotifyPropertyChanged("aid");
}
}
// Define nameColor name: private field, public property, and database column.
private int _rid;
[Column]
public int rid
{
get { return _rid; }
set
{
NotifyPropertyChanging("rid");
_rid = value;
NotifyPropertyChanged("rid");
}
}
private string _title;
[Column]
public string title
{
get { return _title; }
set
{
NotifyPropertyChanging("title");
_title = value;
NotifyPropertyChanged("title");
}
}
private string _thumnail;
[Column]
public string thumnail
{
get { return _thumnail; }
set
{
NotifyPropertyChanging("thumnail");
_thumnail = value;
NotifyPropertyChanged("thumnail");
}
}
private string _DesScription;
[Column(DbType = "NTEXT")]
public string DesScription
{
get { return _DesScription; }
set
{
NotifyPropertyChanging("DesScription");
_DesScription = value;
NotifyPropertyChanged("DesScription");
}
}
private int _orderID;
[Column]
public int orderID
{
get { return _orderID; }
set
{
NotifyPropertyChanging("orderID");
_orderID = value;
NotifyPropertyChanged("orderID");
}
}
private string _pubDate;
[Column]
public string pubDate
{
get { return _pubDate; }
set
{
NotifyPropertyChanging("pubDate");
_pubDate = value;
NotifyPropertyChanged("pubDate");
}
}
private string _linkURL;
[Column]
public string linkURL
{
get { return _linkURL; }
set
{
NotifyPropertyChanging("linkURL");
_linkURL = value;
NotifyPropertyChanged("linkURL");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify that a property changed
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
// Used to notify that a property is about to change
private void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}
when i Update Colum Thumnail , i have erros :
SQL Server does not handle comparison of NText, Text, Xml, or Image data types
because of special characters into database insert sequence trogn should I use BbType = "NTEXT"
Please Help me !
You could remove this column for concurrency checking by adding [Column(UpdateCheck = UpdateCheck.Never)] to this column.
See this blogpost about Linq to sql concurrency checking: http://blogs.msdn.com/b/matt/archive/2008/05/22/into-to-linq-to-sql-optimistic-concurrency.aspx