How to separate data model from validations in WPF? - c#

I used the below code for implementing Model in WPF. But the Problem is that it violates Solid Principle [Design Pattern] because model and validation code both are in the same code.
Help me out to separate them.
Employee Model-
public class EmployeeModel : Model
{
#region Fields
private int employeeId;
private string employeeCode;
private string firstName;
private string lastName;
private DateTime? dateOfJoining;
private DateTime dob;
private string email;
private int? departmentId;
private string departmentName;
private string password;
private string role;
#endregion
#region Public Properties
public int EmployeeId
{
get
{
return employeeId;
}
set
{
if (value != this.employeeId)
{
employeeId = value;
SetPropertyChanged("EmployeeId");
}
}
}
public string EmployeeCode
{
get
{
return employeeCode;
}
set
{
if (value != this.employeeCode)
{
employeeCode = value;
SetPropertyChanged("EmployeeCode");
}
}
}
public DateTime? DateOfJoining
{
get
{
return dateOfJoining;
}
set
{
if (value != this.dateOfJoining)
{
dateOfJoining = Convert.ToDateTime(value);
SetPropertyChanged("DateofJoining");
}
}
}
public string FirstName
{
get
{
return firstName;
}
set
{
if (value != this.firstName)
{
firstName = value;
SetPropertyChanged("FirstName");
}
}
}
public string LastName
{
get
{
return lastName;
}
set
{
if (value != this.lastName)
{
lastName = value;
SetPropertyChanged("LastName");
}
}
}
public string FullName
{
get
{
return string.Join(" ", new[] { firstName, lastName });
}
}
public int? DepartmentId
{
get
{
return departmentId;
}
set
{
if (value != this.departmentId)
{
departmentId = value;
SetPropertyChanged("DepartmentId");
}
}
}
public string DepartmentName
{
get
{
return departmentName;
}
set
{
if (value != this.departmentName)
{
departmentName = value;
SetPropertyChanged("DepartmentName");
}
}
}
public DateTime DOB
{
get
{
return dob;
}
set
{
if (value != this.dob)
{
dob = Convert.ToDateTime(value);
SetPropertyChanged("DateofBirth");
}
}
}
public string Email
{
get
{
return email;
}
set
{
if (value != this.email)
{
email = value;
SetPropertyChanged("Email");
}
}
}
public string Password
{
get
{
return password;
}
set
{
if (value != this.password)
{
password = value;
SetPropertyChanged("Password");
}
}
}
public string Role
{
get
{
return role;
}
set
{
if (value != this.role)
{
role = value;
SetPropertyChanged("Role");
}
}
}
#endregion
#region Private Methods
private bool IsValid(string emailaddress)
{
try
{
MailAddress m = new MailAddress(emailaddress);
return true;
}
catch (Exception)
{
return false;
}
}
#endregion
#region Public Methods
public override string GetErrorForProperty(string propertyName)
{
string retErrorMsg = string.Empty;
switch (propertyName)
{
case "EmployeeCode":
if (EmployeeCode == null || EmployeeCode.Length < 2)
{
retErrorMsg = AppConstants.EmpCodeError;
}
break;
case "FirstName":
if (FirstName == null || FirstName == string.Empty)
{
retErrorMsg = AppConstants.FNameError;
}
break;
case "LastName":
if (LastName == null || LastName == string.Empty)
{
retErrorMsg = AppConstants.LNameError;
}
break;
case "DepartmentId":
if (DepartmentId == null || DepartmentId < 1)
{
retErrorMsg = AppConstants.DepartmentError;
}
break;
case "DOB":
if (DOB.AddYears(60).Date < DateTime.Now.Date || DOB.AddYears(18).Date > DateTime.Now.Date)
{
retErrorMsg = AppConstants.DOBError;
}
break;
case "DateOfJoining":
if (DateOfJoining == null || DateOfJoining > DateTime.Now)
{
retErrorMsg = AppConstants.DOJError;
}
break;
case "Role":
if (!(Role == "A" || Role == "U"))
{
retErrorMsg = AppConstants.RoleError;
}
break;
case "Email":
if (!IsValid(Email))
{
retErrorMsg = AppConstants.EmailError;
}
break;
case "Password":
if ((Password == null || Password.Length < 8))
{
retErrorMsg = AppConstants.PasswordError;
}
break;
}
return retErrorMsg;
}
#endregion
}
Base Class[Model.cs]
public abstract class Model : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
public void SetPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string Error
{
get { return string.Empty; }
}
public string this[string propertyName]
{
get
{
return GetErrorForProperty(propertyName);
}
}
public abstract string GetErrorForProperty(string propertyName);
}

If you're worried about the S in Solid (Single Responsibility Principle), you should take a look at this answer: https://stackoverflow.com/a/597222/1685167
Basically, "The ViewModel single responsibility is to provide the View the information it needs." Personally I think you should be concerned with how the DataModel is unaware of the View more than anything else.

Here's a small example in code:
public class EmployeesEditViewModel : INotifyPropertyChanged
{
public string UserInputNewName
{
get
{
return Model.EmployeName;
}
set
{
if (ValidateValue(value))
{
Model.EmployeName = value;
ValidationResult = string.Empty;
OnPropertyChanged("UserInputNewName");
}
else
{
ValidationResult = "Error, name must not be empty.";
}
}
}
private bool ValidateValue(string value)
{
return !string.IsNullOrWhiteSpace(value);
}
private string _ValidationResult;
public string ValidationResult
{
get
{
return _ValidationResult ?? string.Empty;
}
set
{
_ValidationResult = value;
OnPropertyChanged("ValidationResult");
}
}
private EmployeeModel Model { get; set; }
}
public class EmployeeModel
{
public int EmployeeId { get; set; }
public string EmployeName { get; set; }
}
Explanation:
You have an EmployeeModel, this is the real model that describes an employee. Your view (for example a WPF Windows with user input fields) does not know about the model - there's no direct contact between model an view.
The view only knows about a view model. Suppose this is a mask that allows you to modify employees, then we'll use the EmployeesEditViewModel for that. The ViewModel exposes the properties needed for the view, in this simple case it's just the name of the employee and a valiation result (which could be just displayed in another text field). When the user enters a value, you can check if it's valid and then either update the actual model or tell the user what's wrong.
Ideally, you would probably have some validation logic on the model itself and have the ViewModel only transform this result into something that the user can see. This would keep all the responsibility (like validation) on the model where it belongs, but you'd still have a ViewModel that can translate and forward this to the View.

Related

Getting new items added to an Observable Collection of a custom class

I have a set of classes that I am using to deserialize JSON into. My program will periodically look for changes to this JSON file, and if it finds any, will push the new data to the properties of these classes using reflection.
I need to find any new items added to the collection of the Item2 class (SocialExportJSON.SocialExportData.Item2) after a successful update.
My JSON classes look like this (there are more but I want to avoid too big a wall of code):
public class Item2 : INotifyPropertyChanged
{
[JsonProperty("type")]
private string type;
public string Type
{
get
{
return type;
}
set
{
if (type != value)
{
type = value;
RaisePropertyChanged("Type");
}
}
}
[JsonProperty("id")]
private string id;
public string ID
{
get
{
return id;
}
set
{
if (id != value)
{
id = value;
RaisePropertyChanged("ID");
}
}
}
[JsonProperty("postedIso8601")]
private string postedIso8601;
public string PostedIso8601
{
get
{
return postedIso8601;
}
set
{
if (postedIso8601 != value)
{
postedIso8601 = value;
RaisePropertyChanged("PostedIso8601");
}
}
}
[JsonProperty("postedTimestamp")]
private object postedTimestamp;
public object PostedTimestamp
{
get
{
return postedTimestamp;
}
set
{
if (postedTimestamp != value)
{
postedTimestamp = value;
RaisePropertyChanged("PostedTimestamp");
}
}
}
[JsonProperty("engagement")]
private Engagement engagement;
public Engagement Engagement
{
get
{
return engagement;
}
set
{
if (engagement != value)
{
engagement = value;
RaisePropertyChanged("Engagement");
}
}
}
[JsonProperty("source")]
private Source2 source;
public Source2 Source
{
get
{
return source;
}
set
{
if (source != value)
{
source = value;
RaisePropertyChanged("Source");
}
}
}
[JsonProperty("author")]
private Author author;
public Author Author
{
get
{
return author;
}
set
{
if (author != value)
{
author = value;
RaisePropertyChanged("Author");
}
}
}
[JsonProperty("content")]
private Content content;
public Content Content
{
get
{
return content;
}
set
{
if (content != value)
{
content = value;
RaisePropertyChanged("Content");
}
}
}
[JsonProperty("location")]
private Location location;
public Location Location
{
get
{
return location;
}
set
{
if (location != value)
{
location = value;
RaisePropertyChanged("Location");
}
}
}
[JsonProperty("publication")]
private Publication publication;
public Publication Publication
{
get
{
return publication;
}
set
{
if (publication != value)
{
publication = value;
RaisePropertyChanged("Publication");
}
}
}
[JsonProperty("metadata")]
private Metadata metadata;
public Metadata Metadata
{
get
{
return metadata;
}
set
{
if (metadata != value)
{
metadata = value;
RaisePropertyChanged("Metadata");
}
}
}
//Event handling
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
//Console.WriteLine("Updated");
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
public class SocialExportData : INotifyPropertyChanged
{
[JsonProperty("dataType")]
private string dataType;
public string DataType
{
get
{
return dataType;
}
set
{
if (dataType != value)
{
dataType = value;
RaisePropertyChanged("DataType");
}
}
}
[JsonProperty("id")]
private int id;
public int ID
{
get
{
return id;
}
set
{
if (id != value)
{
id = value;
RaisePropertyChanged("ID");
}
}
}
[JsonProperty("story")]
private Story story;
public Story Story
{
get
{
return story;
}
set
{
if (story != value)
{
story = value;
RaisePropertyChanged("Story");
}
}
}
[JsonProperty("order")]
private string order;
public string Order
{
get
{
return order;
}
set
{
if (order != value)
{
order = value;
RaisePropertyChanged("Order");
}
}
}
[JsonProperty("lifetime")]
private string lifetime;
public string Lifetime
{
get
{
return lifetime;
}
set
{
if (lifetime != value)
{
lifetime = value;
RaisePropertyChanged("Lifetime");
}
}
}
[JsonProperty("maxAge")]
private int maxAge;
public int MaxAge
{
get
{
return maxAge;
}
set
{
if (maxAge != value)
{
maxAge = value;
RaisePropertyChanged("MaxAge");
}
}
}
[JsonProperty("maxSize")]
private int maxSize;
public int MaxSize
{
get
{
return maxSize;
}
set
{
if (maxSize != value)
{
maxSize = value;
RaisePropertyChanged("MaxSize");
}
}
}
[JsonProperty("consumeCount")]
private int consumeCount;
public int ConsumeCount
{
get
{
return consumeCount;
}
set
{
if (consumeCount != value)
{
consumeCount = value;
RaisePropertyChanged("ConsumeCount");
}
}
}
[JsonProperty("consumeInterval")]
private int consumeInterval;
public int ConsumeInterval
{
get
{
return consumeInterval;
}
set
{
if (consumeInterval != value)
{
consumeInterval = value;
RaisePropertyChanged("ConsumeInterval");
}
}
}
[JsonProperty("items")]
private ObservableCollection<Item2> items;
public ObservableCollection<Item2> Items
{
get
{
return items;
}
set
{
if (items != value)
{
items = value;
RaisePropertyChanged("Items");
}
}
}
//Event handling
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
//Console.WriteLine("Updated");
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
public class SocialExportJSON : INotifyPropertyChanged
{
[JsonProperty("id")]
private string id;
public string ID
{
get
{
return id;
}
set
{
if (id != value)
{
id = value;
RaisePropertyChanged("ID");
}
}
}
[JsonProperty("ttl")]
private int ttl;
public int TTL
{
get
{
return ttl;
}
set
{
if (ttl != value)
{
ttl = value;
RaisePropertyChanged("TTL");
}
}
}
[JsonProperty("serial")]
private long serial;
public long Serial
{
get
{
return serial;
}
set
{
if (serial != value)
{
serial = value;
RaisePropertyChanged("Serial");
}
}
}
[JsonProperty("formatType")]
private string formatType;
public string FormatType
{
get
{
return formatType;
}
set
{
if (formatType != value)
{
formatType = value;
RaisePropertyChanged("FormatType");
}
}
}
[JsonProperty("modifiedIso8601")]
private string modifiedIso8601;
public string ModifiedIso8601
{
get
{
return modifiedIso8601;
}
set
{
if (modifiedIso8601 != value)
{
modifiedIso8601 = value;
RaisePropertyChanged("ModifiedIso8601");
}
}
}
[JsonProperty("modifiedTimestamp")]
private long modifiedTimestamp;
public long ModifiedTimestamp
{
get
{
return modifiedTimestamp;
}
set
{
if (modifiedTimestamp != value)
{
modifiedTimestamp = value;
RaisePropertyChanged("ModifiedTimestamp");
}
}
}
[JsonProperty("timezone")]
private string timezone;
public string Timezone
{
get
{
return timezone;
}
set
{
if (timezone != value)
{
timezone = value;
RaisePropertyChanged("Timezone");
}
}
}
[JsonProperty("dataType")]
private string dataType;
public string DataType
{
get
{
return dataType;
}
set
{
if (dataType != value)
{
dataType = value;
RaisePropertyChanged("DataType");
}
}
}
[JsonProperty("exports")]
private ObservableCollection<SocialExportData> exports;
public ObservableCollection<SocialExportData> Exports
{
get
{
return exports;
}
set
{
if (exports != value)
{
exports = value;
RaisePropertyChanged("Exports");
}
}
}
//Event handling
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
//Console.WriteLine("Updated");
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
In another class, I have a method to deserialize to a global instance of my JSON class. It looks like this:
public SocialExportJSON socialExportData;
private async void DownloadAndDeserializeJSONAsync()
{
try
{
//Create a web client with the supplied credentials
var exportClient = new WebClient { Credentials = new NetworkCredential(uName, pw), Encoding = Encoding.UTF8};
//Create a task to download the JSON string and wait for it to finish
var downloadTask = Task.Run(() => exportClient.DownloadString(new Uri(eURL)));
downloadTask.Wait();
//Get the string from the task
var JSONString = await downloadTask;
//Create a task to deserialize the JSON from the last task
var DeserializeTask = Task.Run(() => JsonConvert.DeserializeObject<SocialExportJSON>(JSONString));
DeserializeTask.Wait();
SocialExportJSON sej = await DeserializeTask;
//Check the timestamp first to see if we should change the data
if(socialExportData == null)
{
//Get the data from the task
socialExportData = await DeserializeTask;
}
else if(sej.ModifiedTimestamp != socialExportData.ModifiedTimestamp)
{
//Get the data from the task
SocialExportJSON newData = await DeserializeTask;
GetNewItems(newData);
SetNewData(newData);
//Call the exportUpdated event when the task has finished
exportUpdated();
}
}
catch (Exception e)
{
MessageBox.Show(e.Message.ToString());
}
}
In my SetNewData function, shown below, I use reflection to set the properties of my global class. Because I'm setting the whole collection rather than iterating through each of the properties in each of the classes, I can't use the CollectionChanged event to find new items.
public void SetNewData(SocialExportJSON newData)
{
//Loop through each of the properties and copy from source to target
foreach (PropertyInfo pi in socialExportData.GetType().GetProperties())
{
if (pi.CanWrite)
{
pi.SetValue(socialExportData, pi.GetValue(newData, null), null);
}
}
}
Is there a way I can modify my SetNewData function in such a way that it calls CollectionChanged? If not, what would be the best way to go about getting any new additions to my collection of Item2?
In my Main function. I create an instance of my class called SocialExport like so:
SocialExport s = new SocialExport("http://example.json", "example", "example");.
This class is where the global instance of my JSON class is contained, and my event handler is added like so
s.socialExportData.Exports[0].Items.CollectionChanged += CollectionChanged;
Then you are hooking up an event handler for the CollectionChanged event for that particular instance of ObservableCollection<Item2>.
If you create a new ObservableCollection<Item2>, you obviously must hook up an event handler to this one as well. The event handler that is associated with the old object won't be invoked when new items are added to the new instance.
So whenever a new ObservableCollection<Item2> is created, using deserialization or not, you should hook up a new event handler.
You could probably do this in your DownloadAndDeserializeJSONAsync method. The other option would be to create only one instance of the collection and remove and add items from/to this one.

WCF calls twice or more itself, but only if I keep a code line (I need it)

I have a weird problem. I've made a couple of working wcf. I've updated one of them and it seems all ok.
But, if I call a method with a get within the browser, I get ERR_CONNECTION_RESET. Debbuging it, I've found that the code is looping, calling itself twice or three times, when the call is only one.
The code is really similar to other, but there is something I can't understand.
Why does it loop?
This is the code of my method:
public List<WrapImpianti> GetImpianto(string codret, string storeID, string hashedString, string outputType)
{
Utility.Logger("GetImpianto: start " + outputType);
var md5 = new md5Manager();
string key = "";
IS_RETISTI retista = getRetista(codret);
if (retista != null)
key = retista.CHIAVEDES;
List<WrapImpianti> r = new List<WrapImpianti>();
//return r;
if (md5.checkHashedInput(codret + storeID, hashedString, key))
{
try
{
using (var db = new AccessData.Entities())
{
var t = db.IS_IMPIANTI.Where(x => x.STOREID == storeID && x.IDRET == retista.ID).ToList();
r = t.Select(x => new WrapImpianti(x, key)).ToList();
}
return r;
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message, HttpStatusCode.Conflict);
}
}
else
throw new WebFaultException<string>("Hash non corretto", HttpStatusCode.BadRequest);
}
If I comment the row "r = t.Select(x => new WrapImpianti(x, key)).ToList();", it works... or, at least, it returns an empty object (because I don't populate it) without looping.
Why? It only calls the init method of my class WrapImpianti...
this is the class
[DataContract]
public class WrapImpianti
{
public WrapImpianti(){}
private IS_IMPIANTI model;
private string key;
public WrapImpianti(IS_IMPIANTI model, string key)
{
this.model = model;
this.key = key;
}
private string p_STOREID { get; set; }
[DataMember]
public string STOREID
{
get
{
if (model == null)
return p_STOREID.ToSafeString();
else
return this.model.STOREID.ToSafeString();
}
set { p_STOREID = value; }
}
private string p_CODICE { get; set; }
[DataMember]
public string CODICE
{
get
{
if (model == null)
return p_CODICE.ToSafeString();
else
return this.model.CODICE.ToSafeString();
}
set { p_CODICE = value; }
}
private string p_CODRETE { get; set; }
[DataMember]
public string CODRETE
{
get
{
if (model == null)
return p_CODRETE.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
var pp = db.IS_RETISTI.FirstOrDefault(x => x.ID == this.model.IDRET);
if (pp != null)
return pp.CODICE;
else
return "";
}
}
}
set { p_CODRETE = value; }
}
private string p_INDIRIZZO { get; set; }
[DataMember]
public string INDIRIZZO
{
get
{
if (model == null)
return p_INDIRIZZO.ToSafeString();
else
return this.model.INDIRIZZO.ToSafeString();
}
set { p_INDIRIZZO = value; }
}
private string p_CITTA { get; set; }
[DataMember]
public string CITTA
{
get
{
if (model == null)
return p_CITTA.ToSafeString();
else
return this.model.CITTA.ToSafeString();
}
set { p_CITTA = value; }
}
private string p_STATO { get; set; }
[DataMember]
public string STATO
{
get
{
if (model == null)
return p_STATO.ToSafeString();
else
return this.model.STATO.ToSafeString();
}
set { p_STATO = value; }
}
private string p_COD_ESERCENTE { get; set; }
[DataMember]
public string COD_ESERCENTE
{
get
{
if (model == null)
return p_COD_ESERCENTE.ToSafeString();
else
return this.model.COD_ESERCENTE.ToSafeString();
}
set { p_COD_ESERCENTE = value; }
}
private string p_TERMINAL_ID { get; set; }
[DataMember]
public string TERMINAL_ID
{
get
{
if (model == null)
return p_TERMINAL_ID.ToSafeString();
else
return this.model.TERMINAL_ID.ToSafeString();
}
set { p_TERMINAL_ID = value; }
}
private string p_COL { get; set; }
[DataMember]
public string COL
{
get
{
if (model == null)
return p_COL.ToSafeString();
else
return this.model.COL.ToSafeString();
}
set { p_COL = value; }
}
private string p_NOME { get; set; }
[DataMember]
public string NOME
{
get
{
if (model == null)
return p_NOME.ToSafeString();
else
return this.model.NOME.ToSafeString();
}
set { p_NOME = value; }
}
private string p_CAP { get; set; }
[DataMember]
public string CAP
{
get
{
if (model == null)
return p_CAP.ToSafeString();
else
return this.model.CAP.ToSafeString();
}
set { p_CAP = value; }
}
private string p_TELUFF { get; set; }
[DataMember]
public string TELUFF
{
get
{
if (model == null)
return p_TELUFF.ToSafeString();
else
return this.model.TELUFF.ToSafeString();
}
set { p_TELUFF = value; }
}
private string p_TELCELL { get; set; }
[DataMember]
public string TELCELL
{
get
{
if (model == null)
return p_TELCELL.ToSafeString();
else
return this.model.TELCELL.ToSafeString();
}
set { p_TELCELL = value; }
}
private string p_NOTE { get; set; }
[DataMember]
public string NOTE
{
get
{
if (model == null)
return p_NOTE.ToSafeString();
else
return this.model.NOTE.ToSafeString();
}
set { p_NOTE = value; }
}
private string p_PIVA { get; set; }
[DataMember]
public string PIVA
{
get
{
if (model == null)
return p_PIVA.ToSafeString();
else
return this.model.PIVA.ToSafeString();
}
set { p_PIVA = value; }
}
private string p_CODICEFISCALE { get; set; }
[DataMember]
public string CODICEFISCALE
{
get
{
if (model == null)
return p_CODICEFISCALE.ToSafeString();
else
return this.model.CODICEFISCALE.ToSafeString();
}
set { p_CODICEFISCALE = value; }
}
private string p_GESTORE { get; set; }
[DataMember]
public string GESTORE
{
get
{
if (model == null)
return p_GESTORE.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
return db.IS_GESTORI.FirstOrDefault(x => x.ID == this.model.IDGES).CODICE.ToSafeString();
}
}
}
set { p_GESTORE = value; }
}
private string p_PROPRIETARIO { get; set; }
[DataMember]
public string PROPRIETARIO
{
get
{
if (model == null)
return p_PROPRIETARIO.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
return db.IS_PROPRIETARI.FirstOrDefault(x => x.ID == this.model.IDPRP).CODICE.ToSafeString();
}
}
}
set { p_PROPRIETARIO = value; }
}
}
Why, if I call my class init method, does it start looping? And then, why does it return ERR_CONNECTION_RESET?
Ok, this is interesting. The solutions was here:
public string PROPRIETARIO
{
get
{
if (model == null)
return p_PROPRIETARIO.ToSafeString();
else
{
using (var db = new AccessData.Entities())
{
return db.IS_PROPRIETARI.FirstOrDefault(x => x.ID == this.model.IDPRP).CODICE.ToSafeString();
}
}
}
set { p_PROPRIETARIO = value; }
in my customclass.. sometimes, the IDPRP is null and raises this error.
It is very interisting: I was expecting this type of error was more visible to the developer, instead of loop itself and get an anonymous error.
Hope it can be useful for someone. ;)

Why my ICollection is always empty?

I am trying to reach a foreach but my program never gets inside because my ICollection Coletores is always empty even if I put a lot of stuff in there.
The code where I want to get inside and it is always empty (I want to get inside the second foreach):
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (NavigationContext.QueryString.TryGetValue("email", out email))
{
//MessageBox.Show(email);
List<HyperlinkButton> listaLinks = new List<HyperlinkButton>();
AppDataContext db = new AppDataContext();
int i = 0;
HyperlinkButton aux = new HyperlinkButton();
foreach (Pessoa pessoa in db.Pessoas)
{
if (pessoa.Email == email)
{
foreach (Coletor coletor in pessoa.Coletores)
{
aux.Content = "Coletor " + (i + 1).ToString();
aux.FontSize = 24;
aux.NavigateUri = new Uri("/OcorrenciasPage.xaml?coletorId=" + coletor.Id.ToString(), UriKind.RelativeOrAbsolute);
listaLinks.Add(aux);
i++;
}
}
}
ListBox coletores = new ListBox();
coletores.ItemsSource = listaLinks;
stcList.Children.Add(coletores);
}
base.OnNavigatedTo(e);
}
Now the code that I am adding data:
if (txtLat.Text != "" && txtLong.Text != "" && rdNorte.IsChecked == true || rdSul.IsChecked == true && rdLeste.IsChecked == true || rdOeste.IsChecked == true)
{
foreach (var pessoa in db.Pessoas)
{
if (pessoa.Email == email)
{
pessoa.Coletores.Add(coletor);
}
}
db.Coletores.InsertOnSubmit(coletor);
db.SubmitChanges();
NavigationService.Navigate(new Uri("/ColetoresPage.xaml?email=" + email, UriKind.RelativeOrAbsolute));
}
Now, my Pessoa class:
public class Pessoa : INotifyPropertyChanged
{
private int _id;
[Column(IsDbGenerated = true, IsPrimaryKey = true)]
public int Id {
get { return _id;}
set { _id = value;
OnPropertyChanged("Id");
}
}
private string _nome;
[Column]
public string Nome
{
get { return _nome; }
set { _nome = value;
OnPropertyChanged("Nome");
}
}
private string _email;
[Column]
public string Email
{
get { return _email; }
set { _email = value;
OnPropertyChanged("Email");
}
}
private string _senha;
[Column]
public string Senha { get { return _senha; }
set { _senha = value;
OnPropertyChanged("Senha");
}
}
private string _profissao;
[Column]
public string Profissao { get { return _profissao; }
set { _profissao = value;
OnPropertyChanged("Profissao");
}
}
private int _idade;
[Column]
public int Idade { get { return _idade; }
set { _idade = value;
OnPropertyChanged("Idade");
}
}
private string _endereco;
[Column]
public string Endereco { get { return _endereco; }
set { _endereco = value;
OnPropertyChanged("Endereco");
}
}
private string _cidade;
[Column]
public string Cidade { get { return _cidade; }
set { _cidade = value;
OnPropertyChanged("Cidade");
}
}
private string _estado;
[Column]
public string Estado { get { return _estado; }
set { _estado = value;
OnPropertyChanged("Estado");
}
}
private EntitySet<Coletor> _coletores = new EntitySet<Coletor>();
[Association(Name = "FK_Coletores_PessoaColetores", Storage = "_coletores", ThisKey = "Id", OtherKey = "pessoaId")]
public ICollection<Coletor> Coletores
{
get { return _coletores; }
set { _coletores.Assign(value); }
}
private EntitySet<PessoaColetor> _pessoaColetores = new EntitySet<PessoaColetor>();
[Association(Name = "FK_PessoaColetores_Pessoas", Storage = "_pessoaColetores", OtherKey = "pessoaId", ThisKey = "Id")]
private ICollection<PessoaColetor> PessoaColetores
{
get { return _pessoaColetores; }
set { _pessoaColetores.Assign(value); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
The Coletores property on the Pessoa class looks alright. There is also a PessoaColetores property. Are you sure there has been no confusion between the two? Especially with intellisense one might dot or tab the wrong one.
Have you put a break point on the line that actually adds coletors (second code snippet)? Maybe stuff is added to the wrong collection.
Cheers, B.

Entity Framework 6.1: CRUD on Child objects

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();
}

How to use MetadataTypeAttribute with extended classes

I want to add a DisplayAttribute to the Client entity (from another project), but don't want to pollute my entity with attributes specific to MVC or a UI layer. So I planned to add the DisplayAttribute by applying a metadata class to a view model inheriting from the entity
If I inherit from the Client entity and then try to use the MetadataTypeAttribute to add a display attribute, it doesn't show up in the browser. Does anyone know how I can achieve the separation and the functionality of being able to add metadata to my entities?
The Client entity class:
public class Client
{
private string firstName;
private string lastName;
private string homeTelephone;
private string workTelephone;
private string mobileTelephone;
private string emailAddress;
private string notes;
public Title Title { get; set; }
public string FirstName
{
get { return this.firstName ?? string.Empty; }
set { this.firstName = value; }
}
public string LastName
{
get { return this.lastName ?? string.Empty; }
set { this.lastName = value; }
}
public string FullName
{
get
{
List<string> nameParts = new List<string>();
if (this.Title != Title.None)
{
nameParts.Add(this.Title.ToString());
}
if (this.FirstName.Length > 0)
{
nameParts.Add(this.FirstName.ToString());
}
if (this.LastName.Length > 0)
{
nameParts.Add(this.LastName.ToString());
}
return string.Join(" ", nameParts);
}
}
public Address Address { get; set; }
public string HomeTelephone
{
get { return this.homeTelephone ?? string.Empty; }
set { this.homeTelephone = value; }
}
public string WorkTelephone
{
get { return this.workTelephone ?? string.Empty; }
set { this.workTelephone = value; }
}
public string MobileTelephone
{
get { return this.mobileTelephone ?? string.Empty; }
set { this.mobileTelephone = value; }
}
public string EmailAddress
{
get { return this.emailAddress ?? string.Empty; }
set { this.emailAddress = value; }
}
public string Notes
{
get { return this.notes ?? string.Empty; }
set { this.notes = value; }
}
public Client()
{
this.Address = new Address();
}
}
The ClientViewModel view model class:
[MetadataType(typeof(ClientMetaData))]
public class ClientViewModel : Client
{
internal class ClientMetaData
{
[Display(ResourceType = typeof(ResourceStrings), Name = "Client_FirstName_Label")]
public string FirstName { get; set; }
}
}
I think you have change the typeof parameter to:
[MetadataType(typeof(ClientViewModel.ClientMetaData))]
public class ClientViewModel : Client
{
internal class ClientMetaData
{
[Display(ResourceType = typeof(ResourceStrings), Name = "Client_FirstName_Label")]
public string FirstName { get; set; }
}
}
For .Net Core 6.0 use
[ModelMetadataType(typeof(ClientViewModel.ClientMetaData))]
insead of
[MetadataType(typeof(ClientViewModel.ClientMetaData))]

Categories

Resources