Disable save button when validation fails - c#

As you can likely see from the title, I am about to ask something which has been asked many times before. But still, after reading all these other questions, I cannot find a decent solution to my problem.
I have a model class with basic validation:
partial class Player : IDataErrorInfo
{
public bool CanSave { get; set; }
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Firstname")
{
if (String.IsNullOrWhiteSpace(Firstname))
{
result = "Geef een voornaam in";
}
}
if (columnName == "Lastname")
{
if (String.IsNullOrWhiteSpace(Lastname))
{
result = "Geef een familienaam in";
}
}
if (columnName == "Email")
{
try
{
MailAddress email = new MailAddress(Email);
}
catch (FormatException)
{
result = "Geef een geldig e-mailadres in";
}
}
if (columnName == "Birthdate")
{
if (Birthdate.Value.Date >= DateTime.Now.Date)
{
result = "Geef een geldige geboortedatum in";
}
}
CanSave = true; // this line is wrong
return result;
}
}
public string Error { get { throw new NotImplementedException();} }
}
This validation is done everytime the property changes (so everytime the user types a character in the textbox):
<TextBox Text="{Binding CurrentPlayer.Firstname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="137" IsEnabled="{Binding Editing}" Grid.Row="1"/>
This works perfect. The validation occurs (the PropertyChanged code for the binding is done in the VM on the CurrentPlayer property, which is an object of Player).
What I would like to do now is disable the save button when the validation fails.
First of all, the easiest solutions seems to be found in this thread:
Enable Disable save button during Validation using IDataErrorInfo
If I want to follow the accepted solution, I'd have to write my
validation code twice, as I cannot simply use the indexer. Writing
double code is absolutely not what I want, so that's not a solution
to my problem.
The second answer on that thread sounded very promising as first,
but the problem is that I have multiple fields that have to be
validated. That way, everything relies on the last checked property
(so if that field is filled in correctly, CanSave will be true, even
though there are other fields which are still invalid).
One more solution I've found is using an ErrorCount property. But as I'm validating at each property change (and so at each typed character), this isn't possible too - how could I know when to increase/decrease the ErrorCount?
What would be the best way to solve this problem?
Thanks

This article http://www.asp.net/mvc/tutorials/older-versions/models-%28data%29/validating-with-the-idataerrorinfo-interface-cs moves the individual validation into the properties:
public partial class Player : IDataErrorInfo
{
Dictionary<string, string> _errorInfo;
public Player()
{
_errorInfo = new Dictionary<string, string>();
}
public bool CanSave { get { return _errorInfo.Count == 0; }
public string this[string columnName]
{
get
{
return _errorInfo.ContainsKey(columnName) ? _errorInfo[columnName] : null;
}
}
public string FirstName
{
get { return _firstName;}
set
{
if (String.IsNullOrWhiteSpace(value))
_errorInfo.AddOrUpdate("FirstName", "Geef een voornaam in");
else
{
_errorInfo.Remove("FirstName");
_firstName = value;
}
}
}
}
(you would have to handle the Dictionary AddOrUpdate extension method). This is similar to your error count idea.

I've implemented the map approach shown in my comment above, in C# this is called a Dictionary in which I am using anonymous methods to do the validation:
partial class Player : IDataErrorInfo
{
private delegate string Validation(string value);
private Dictionary<string, Validation> columnValidations;
public List<string> Errors;
public Player()
{
columnValidations = new Dictionary<string, Validation>();
columnValidations["Firstname"] = delegate (string value) {
return String.IsNullOrWhiteSpace(Firstname) ? "Geef een voornaam in" : null;
}; // Add the others...
errors = new List<string>();
}
public bool CanSave { get { return Errors.Count == 0; } }
public string this[string columnName]
{
get { return this.GetProperty(columnName); }
set
{
var error = columnValidations[columnName](value);
if (String.IsNullOrWhiteSpace(error))
errors.Add(error);
else
this.SetProperty(columnName, value);
}
}
}

This approach works with Data Annotations. You can also bind the "IsValid" property to a Save button to enable/disable.
public abstract class ObservableBase : INotifyPropertyChanged, IDataErrorInfo
{
#region Members
private readonly Dictionary<string, string> errors = new Dictionary<string, string>();
#endregion
#region Events
/// <summary>
/// Property Changed Event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Protected Methods
/// <summary>
/// Get the string name for the property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
protected string GetPropertyName<T>(Expression<Func<T>> expression)
{
var memberExpression = (MemberExpression) expression.Body;
return memberExpression.Member.Name;
}
/// <summary>
/// Notify Property Changed (Shorted method name)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
protected virtual void Notify<T>(Expression<Func<T>> expression)
{
string propertyName = this.GetPropertyName(expression);
PropertyChangedEventHandler handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Called when [property changed].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression.</param>
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> expression)
{
string propertyName = this.GetPropertyName(expression);
PropertyChangedEventHandler handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Properties
/// <summary>
/// Gets an error message indicating what is wrong with this object.
/// </summary>
public string Error => null;
/// <summary>
/// Returns true if ... is valid.
/// </summary>
/// <value>
/// <c>true</c> if this instance is valid; otherwise, <c>false</c>.
/// </value>
public bool IsValid => this.errors.Count == 0;
#endregion
#region Indexer
/// <summary>
/// Gets the <see cref="System.String"/> with the specified column name.
/// </summary>
/// <value>
/// The <see cref="System.String"/>.
/// </value>
/// <param name="columnName">Name of the column.</param>
/// <returns></returns>
public string this[string columnName]
{
get
{
var validationResults = new List<ValidationResult>();
string error = null;
if (Validator.TryValidateProperty(GetType().GetProperty(columnName).GetValue(this), new ValidationContext(this) { MemberName = columnName }, validationResults))
{
this.errors.Remove(columnName);
}
else
{
error = validationResults.First().ErrorMessage;
if (this.errors.ContainsKey(columnName))
{
this.errors[columnName] = error;
}
else
{
this.errors.Add(columnName, error);
}
}
this.OnPropertyChanged(() => this.IsValid);
return error;
}
}
#endregion
}

Related

System.Reflection.TargetInvocationException in constructor

I am keep getting
System.Reflection.TargetInvocationException
and PresentationFramework.dll, additional info Exception has been thrown by the target of an invocation.
Can someone please help me out here?
Info:
Call Stack
PresentationFramework.dll!System.Windows.Markup.WpfXamlLoader.Load(System.Xaml.XamlReader xamlReader, System.Xaml.IXamlObjectWriterFactory writerFactory, bool skipJournaledProperties, object rootObject, System.Xaml.XamlObjectWriterSettings settings, System.Uri baseUri) Unknown
namespace PMD.Analysis.AnalysisViewModel
{
using PMD.Measurement.MeasurementModel;
using System.Windows.Data;
using PMD.Analysis.AnalysisModel;
using System;
using System.Collections.Generic;
using PMD.Measurement.MeasurementViewModel;
public class AnalysisViewModel : ViewModel
{
/// <summary>
/// New analysis command.
/// </summary>
private ICommand newAnalysis = null;
public PMD.Analysis.AnalysisViewModel.NewAnalysisViewModel m_NewAnalysisViewModel;
Measurement measurement = new Measurement();
private ICollectionView measurements = null;
/// <summary>
/// Measurement's search by title field.
/// </summary>
private string searchTitle;
/// <summary>
/// Measurement's search by title field.
/// </summary>
private string searchTester;
/// <summary>
/// Measurement's search by vehicle VIN field.
/// </summary>
private string searchVehicleVIN;
public MeasurementModel MeasurementModel
{
get;
set;
}
public enum SelectedState
{
// No Masurements.
Inactive,
// Masurements.
Active,
// Waiting for Masurements.
WaitingAnswer
};
public SelectedState CurrentSelectedState { get; set; }
public Analysis Analysis
{
get;
set;
}
public AnalysisViewModel()
{
Analysis = new Analysis();
measurements = new ListCollectionView(MeasurementModel.Measurements);
measurements.Filter = new Predicate<object>(SearchCallbackAnalysis);
}
~AnalysisViewModel()
{
}
/// <summary>
/// List of measurements that will be displayed in analysis view.
/// </summary>
public ICollectionView Measurements
{
get { return measurements; }
set { measurements = value; }
}
/// <summary>
/// Gets or sets new analysis command.
/// </summary>
public ICommand NewAnalysis
{
get
{
if (newAnalysis == null)
newAnalysis = new NewAnalysisCommand(this);
return newAnalysis;
}
}
public bool SearchCallbackAnalysis(object item)
{
bool isItemShowed = true;
if ((searchTitle != "") && (searchTitle != null))
isItemShowed &= (((Measurement)item).Title == searchTitle);
if ((searchVehicleVIN != "") && (searchVehicleVIN != null))
isItemShowed &= (((Measurement)item).Vehicle.VehicleVIN == searchVehicleVIN);
if ((SearchTester != "") && (SearchTester != null))
isItemShowed &= (((Measurement)item).Tester == SearchTester);
return isItemShowed;
}
/// <summary>
/// Gets or sets measurement's search by title field.
/// </summary>
public string SearchTitle
{
get
{
return searchTitle;
}
set
{
searchTitle = value;
Measurements.Refresh();
}
}
/// <summary>
/// Gets or sets measurement's search by tester name field.
/// </summary>
public string SearchTester
{
get
{
return searchTester;
}
set
{
searchTester = value;
Measurements.Refresh();
}
}
/// <summary>
/// Gets or sets measurement's search by vehicle VIN field.
/// </summary>
public string SearchVehicleVIN
{
get
{
return searchVehicleVIN;
}
set
{
searchVehicleVIN = value;
Measurements.Refresh();
}
}
}//end AnalysisViewModel
}//end namespace AnalysisViewModel
if i comment in constructor this line of code:
measurements.Filter = new Predicate<object>(SearchCallbackAnalysis);
Everything works fine but i need this line to search in the list.
Additional info:
xamlReader Cannot obtain value of local or argument 'xamlReader' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Xaml.XamlReader
writerFactory Cannot obtain value of local or argument 'writerFactory' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Xaml.IXamlObjectWriterFactory
skipJournaledProperties Cannot obtain value of local or argument 'skipJournaledProperties' as it is not available at this instruction pointer, possibly because it has been optimized away. bool
rootObject Cannot obtain value of local or argument 'rootObject' as it is not available at this instruction pointer, possibly because it has been optimized away. object
settings Cannot obtain value of local or argument 'settings' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Xaml.XamlObjectWriterSettings
baseUri Cannot obtain value of local or argument 'baseUri' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Uri
i have try:
public ICollectionView Measurements
{
get { return measurements; }
set { measurements = value;
measurements.Filter = new Predicate<object>(SearchCallbackAnalysis);
}
}
Now everything works fine. Thank you for try to help me.

C# Base Class for tracking changes ADO.NET

I'm using Entity Framework in my application with ChangeTracking Entities so I can have an Audit Log in my database for every Add, Modification, Delete (ColumnName, OriginalValue, NewValue, etc).
There are some functions that I'm not going to use Entity Framework because is very slow, for example bulk insert / update and other scenarios, instead I will user ADO.NET with Stored Procedures.
In order to keep tracking changes on both Entity Framework and ADO.NET I was thinking on use a Base Class for custom classes that will be populated from DataReaders and will need to track changes on those classes so I came up with:
public class NotifyPropertyChangeObject : INotifyPropertyChanged
{
/// <summary>
/// Track changes or not.
/// If we're working with DTOs and we fill up the DTO in the DAL we should not be tracking changes.
/// </summary>
private bool trackChanges = false;
/// <summary>
/// Changes to the object
/// </summary>
public List<TrackChange> Changes { get; private set; }
/// <summary>
/// Is the object dirty or not?
/// </summary>
public bool IsDirty
{
get { return Changes.Count > 0; }
set { ; }
}
/// <summary>
/// Event required for INotifyPropertyChanged
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// This constructor will initialize the change tracking
/// </summary>
public NotifyPropertyChangeObject()
{
// Change tracking default
trackChanges = true;
// New change tracking dictionary
Changes = new List<TrackChange>();
}
/// <summary>
/// Reset the object to non-dirty
/// </summary>
public void Reset()
{
Changes.Clear();
}
/// <summary>
/// Start tracking changes
/// </summary>
public void StartTracking()
{
trackChanges = true;
}
/// <summary>
/// Stop tracking changes
/// </summary>
public void StopTracking()
{
trackChanges = false;
}
/// <summary>
/// Change the property if required and throw event
/// </summary>
/// <param name="variable"></param>
/// <param name="property"></param>
/// <param name="value"></param>
public void ApplyPropertyChange<T, F>(ref F field, Expression<Func<T, object>> property, F value)
{
// Only do this if the value changes
if (field == null || !field.Equals(value))
{
// Get the property
var propertyExpression = GetMemberExpression(property);
if (propertyExpression == null)
throw new InvalidOperationException("You must specify a property");
// Property name
string propertyName = propertyExpression.Member.Name;
// If change tracking is enabled, we can track the changes...
if (trackChanges)
{
// Change tracking
var track = Changes.Where(c => c.Name == propertyName).FirstOrDefault();
if (track == null)
{
track = new TrackChange();
track.Name = propertyName;
track.Original = field;
track.Value = value;
Changes.Add(track);
}
else
{
track.Name = propertyName;
track.Original = field;
track.Value = value;
}
//Changes[propertyName] = value;
// Notify change
NotifyPropertyChanged(propertyName);
}
// Set the value
field = value;
}
}
/// <summary>
/// Get member expression
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
public MemberExpression GetMemberExpression<T>(Expression<Func<T, object>> expression)
{
// Default expression
MemberExpression memberExpression = null;
// Convert
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
// Member access
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
// Not a member access
if (memberExpression == null)
throw new ArgumentException("Not a member access", "expression");
// Return the member expression
return memberExpression;
}
/// <summary>
/// The property has changed
/// </summary>
/// <param name="propertyName"></param>
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Convert the changes to an XML string
/// </summary>
/// <returns></returns>
public string ChangesToXml()
{
// Prepare base objects
XDeclaration declaration = new XDeclaration("1.0", Encoding.UTF8.HeaderName, String.Empty);
XElement root = new XElement("Changes");
// Create document
XDocument document = new XDocument(declaration, root);
// Add changes to the document
// TODO: If it's an object, maybe do some other things
//foreach (KeyValuePair<string, object> change in Changes)
// root.Add(new XElement(change.Key, change.Value));
// Get the XML
return document.Document.ToString();
}
}
public class TrackChange
{
public string Name { get; set; }
public object Original { get; set; }
public object Value { get; set; }
}
That way I can keep my Audit Log functionality on both Data Context.
When saving data using ADO.NET I will have a function that will convert my Changes into Stored Procedures parameters so I can save into my AuditLog table.
Does anyone know if this is a good alternative?

Comparing a List<T> with another List<t>

I have been reading on how to compare a list with one annother. I have tried to implement the IEquatable interface. Here is what i have done so far:
/// <summary>
/// A object holder that contains a service and its current failcount
/// </summary>
public class ServiceHolder : IEquatable<ServiceHolder>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="service"></param>
public ServiceHolder(Service service)
{
Service = service;
CurrentFailCount = 0;
}
public Service Service { get; set; }
public UInt16 CurrentFailCount { get; set; }
/// <summary>
/// Public equal method
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
ServiceHolder tmp = obj as ServiceHolder;
if (tmp == null)
{
return false;
}
else
{
return Equals(tmp);
}
}
/// <summary>
/// Checks the internal components compared to one annother
/// </summary>
/// <param name="serviceHolder"></param>
/// <returns>tru eif they are the same else false</returns>
public bool Equals(ServiceHolder serviceHolder)
{
if (serviceHolder == null)
{
return false;
}
if (this.Service.Id == serviceHolder.Service.Id)
{
if (this.Service.IpAddress == serviceHolder.Service.IpAddress)
{
if (this.Service.Port == serviceHolder.Service.Port)
{
if (this.Service.PollInterval == serviceHolder.Service.PollInterval)
{
if (this.Service.ServiceType == serviceHolder.Service.ServiceType)
{
if (this.Service.Location == serviceHolder.Service.Location)
{
if (this.Service.Name == this.Service.Name)
{
return true;
}
}
}
}
}
}
}
return false;
}
}
and this is where I use it:
private void CheckIfServicesHaveChangedEvent()
{
IList<ServiceHolder> tmp;
using (var db = new EFServiceRepository())
{
tmp = GetServiceHolders(db.GetAll());
}
if (tmp.Equals(Services))
{
StateChanged = true;
}
else
{
StateChanged = false;
}
}
Now when I debug and I put a break point in the equals function it never gets hit.
This leads me to think I have implemented it incorrectly or Im not calling it correctly?
If you want to compare the contents of two lists then the best method is SequenceEqual.
if (tmp.SequenceEquals(Services))
This will compare the contents of both lists using equality semantics on the values in the list. In this case the element type is ServiceHolder and as you've already defined equality semantics for this type it should work just fine
EDIT
OP commented that order of the collections shouldn't matter. For that scenario you can do the following
if (!tmp.Except(Services).Any())
You can compare lists without the order most easily with linq.
List<ServiceHolder> result = tmp.Except(Services).ToList();

C# Immutable & Mutable types without duplication

Given the following implementation of mutable and immutable types, is there a way to avoid duplicate code (mainly the duplicate properties)?
I'd like to work with immutable type by default unless a mutable type is required (e.g. when binding to UI elements).
We're using .NET framework 4.0, but plan switching to 4.5 soon.
public class Person {
public string Name { get; private set; }
public List<string> Jobs { get; private set; } // Change to ReadOnlyList<T>
public Person() {}
public Person(Mutable m) {
Name = m.Name;
}
public class Mutable : INotifyPropertyChanged {
public string Name { get; set; }
public List<string> Jobs { get; set; }
public Mutable() {
Jobs = new List<string>();
}
public Mutable(Person p) {
Name = p.Name;
Jobs = new List<string>(p.Jobs);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
// TODO: implement
}
}
}
public class Consumer {
public Consumer() {
// We can use object initializers :)
Person.Mutable m = new Person.Mutable {
Name = "M. Utable"
};
// Consumers can happily mutate away....
m.Name = "M. Utated";
m.Jobs.Add("Herper");
m.Jobs.Add("Derper");
// But the core of our app only deals with "realio-trulio" immutable types.
// Yey! Have constructor with arity of one as opposed to
// new Person(firstName, lastName, email, address, im, phone)
Person im = new Person(m);
}
}
I made something that does what you ask recently (using T4 templates), so it is absolutely possible:
https://github.com/xaviergonz/T4Immutable
For example, given this:
[ImmutableClass(Options = ImmutableClassOptions.IncludeOperatorEquals)]
class Person {
private const int AgeDefaultValue = 18;
public string FirstName { get; }
public string LastName { get; }
public int Age { get; }
[ComputedProperty]
public string FullName {
get {
return FirstName + " " + LastName;
}
}
}
It will automatically generate for you in a separate partial class file the following:
A constructor such as public Person(string firstName, string
lastName, int age = 18) that will initialize the values.
Working implementations for Equals(object other) and Equals(Person other).
Also it will add the IEquatable interface for you. Working
implementations for operator== and operator!=
A working implementation of GetHashCode() A better ToString() with output such as "Person { FirstName=John, LastName=Doe, Age=21 }"
A Person With(...) method that can be used to generate a new immutable clone with 0 or more properties changed (e.g. var janeDoe = johnDoe.With(firstName: "Jane", age: 20)
So it will generate this (excluding some redundant attributes):
using System;
partial class Person : IEquatable<Person> {
public Person(string firstName, string lastName, int age = 18) {
this.FirstName = firstName;
this.LastName = lastName;
this.Age = age;
_ImmutableHashCode = new { this.FirstName, this.LastName, this.Age }.GetHashCode();
}
private bool ImmutableEquals(Person obj) {
if (ReferenceEquals(this, obj)) return true;
if (ReferenceEquals(obj, null)) return false;
return T4Immutable.Helpers.AreEqual(this.FirstName, obj.FirstName) && T4Immutable.Helpers.AreEqual(this.LastName, obj.LastName) && T4Immutable.Helpers.AreEqual(this.Age, obj.Age);
}
public override bool Equals(object obj) {
return ImmutableEquals(obj as Person);
}
public bool Equals(Person obj) {
return ImmutableEquals(obj);
}
public static bool operator ==(Person a, Person b) {
return T4Immutable.Helpers.AreEqual(a, b);
}
public static bool operator !=(Person a, Person b) {
return !T4Immutable.Helpers.AreEqual(a, b);
}
private readonly int _ImmutableHashCode;
private int ImmutableGetHashCode() {
return _ImmutableHashCode;
}
public override int GetHashCode() {
return ImmutableGetHashCode();
}
private string ImmutableToString() {
var sb = new System.Text.StringBuilder();
sb.Append(nameof(Person) + " { ");
var values = new string[] {
nameof(this.FirstName) + "=" + T4Immutable.Helpers.ToString(this.FirstName),
nameof(this.LastName) + "=" + T4Immutable.Helpers.ToString(this.LastName),
nameof(this.Age) + "=" + T4Immutable.Helpers.ToString(this.Age),
};
sb.Append(string.Join(", ", values) + " }");
return sb.ToString();
}
public override string ToString() {
return ImmutableToString();
}
private Person ImmutableWith(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return new Person(
!firstName.HasValue ? this.FirstName : firstName.Value,
!lastName.HasValue ? this.LastName : lastName.Value,
!age.HasValue ? this.Age : age.Value
);
}
public Person With(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return ImmutableWith(firstName, lastName, age);
}
}
And there are some more features as explained in the project page.
PS: If you want a property that's a list of other immutable objects just add:
public ImmutableList<string> Jobs { get; }
No, there's no easy way to avoid duplicate code.
What you've implemented is effectivly the builder pattern. The .NET StringBuilder class follows the same approach.
The support for immutable types in C# is a bit lacking, and could do with some language specific features to make it easier. Having to create a builder is a real pain, as you've discovred. An alternative is to have a constructor that takes all the values, but you tend to end up with the mother of all constructors, which makes the code unreadable.
Since the properties don't have the same visibility, this is not duplicate code. If their visibilty were the same, Person could inherit from Mutable to avoid duplication. Right now, I don't think there is code to factorize in what you show.
Think about using code generation to map each mutable to its immutable equivalent.
I personally like T4 code generation, aided by T4Toolbox library.
You can quite easily parse your code using EnvDTE.
You can find tons of high-quality information about T4 on Oleg Sych blog
http://www.olegsych.com/
Code generation could be hard to handle in the beginning, but it solves the infamous issue of the code-that-must-be-duplicated.
One for your consideration depending on if you're creating a public facing API is to consider 'popcicle immutability' as discussed by Eric Lippert. The nice thing about this is you don't need any duplication at all.
I've used something in reverse, my classes are mutable until a certain point when some calculations are going to occur at which point I call a Freeze() method. All changes to properties call a BeforeValueChanged() method, which if frozen throws an exception.
What you need is something whereby the classes are frozen by default, and you unfreeze them if you need them mutable. As others have mentioned if frozen you need to be returning read only copies of lists etc.
Here's an example of a little class I put together:
/// <summary>
/// Defines an object that has a modifiable (thawed) state and a read-only (frozen) state
/// </summary>
/// <remarks>
/// All derived classes should call <see cref="BeforeValueChanged"/> before modifying any state of the object. This
/// ensures that a frozen object is not modified unexpectedly.
/// </remarks>
/// <example>
/// This sample show how a derived class should always use the BeforeValueChanged method <see cref="BeforeValueChanged"/> method.
/// <code>
/// public class TestClass : Freezable
/// {
/// public String Name
/// {
/// get { return this.name; }
/// set
/// {
/// BeforeValueChanged();
/// this.name = name;
/// }
/// }
/// private string name;
/// }
/// </code>
/// </example>
[Serializable]
public class Freezable
{
#region Locals
/// <summary>Is the current instance frozen?</summary>
[NonSerialized]
private Boolean _isFrozen;
/// <summary>Can the current instance be thawed?</summary>
[NonSerialized]
private Boolean _canThaw = true;
/// <summary>Can the current instance be frozen?</summary>
[NonSerialized]
private Boolean _canFreeze = true;
#endregion
#region Properties
/// <summary>
/// Gets a value that indicates whether the object is currently modifiable.
/// </summary>
/// <value>
/// <c>true</c> if this instance is frozen; otherwise, <c>false</c>.
/// </value>
public Boolean IsFrozen
{
get { return this._isFrozen; }
private set { this._isFrozen = value; }
}
/// <summary>
/// Gets a value indicating whether this instance can be frozen.
/// </summary>
/// <value>
/// <c>true</c> if this instance can be frozen; otherwise, <c>false</c>.
/// </value>
public Boolean CanFreeze
{
get { return this._canFreeze; }
private set { this._canFreeze = value; }
}
/// <summary>
/// Gets a value indicating whether this instance can be thawed.
/// </summary>
/// <value>
/// <c>true</c> if this instance can be thawed; otherwise, <c>false</c>.
/// </value>
public Boolean CanThaw
{
get { return this._canThaw; }
private set { this._canThaw = value; }
}
#endregion
#region Methods
/// <summary>
/// Freeze the current instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance can not be frozen for any reason.</exception>
public void Freeze()
{
if (this.CanFreeze == false)
throw new InvalidOperationException("The instance can not be frozen at this time.");
this.IsFrozen = true;
}
/// <summary>
/// Does a Deep Freeze for the duration of an operation, preventing it being thawed while the operation is running.
/// </summary>
/// <param name="operation">The operation to run</param>
internal void DeepFreeze(Action operation)
{
try
{
this.DeepFreeze();
operation();
}
finally
{
this.DeepThaw();
}
}
/// <summary>
/// Applies a Deep Freeze of the current instance, preventing it be thawed, unless done deeply.
/// </summary>
internal void DeepFreeze()
{
// Prevent Light Thawing
this.CanThaw = false;
this.Freeze();
}
/// <summary>
/// Applies a Deep Thaw of the current instance, reverting a Deep Freeze.
/// </summary>
internal void DeepThaw()
{
// Enable Light Thawing
this.CanThaw = true;
this.Thaw();
}
/// <summary>
/// Thaws the current instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance can not be thawed for any reason.</exception>
public void Thaw()
{
if (this.CanThaw == false)
throw new InvalidOperationException("The instance can not be thawed at this time.");
this.IsFrozen = false;
}
/// <summary>
/// Ensures that the instance is not frozen, throwing an exception if modification is currently disallowed.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance is currently frozen and can not be modified.</exception>
protected void BeforeValueChanged()
{
if (this.IsFrozen)
throw new InvalidOperationException("Unable to modify a frozen object");
}
#endregion
}

Include not working with Entity Framework query

I'm not sure what changed, but, after coming back to an application I was working on a few weeks ago, my .Include() call is no longer working for for one of my related tables. The weird part is that it works for a different table. Here is some code with comments showing what my results are:
//Get the order and nothing else.
using (OrderEntity orderContext = new OrderEntity(OrdersConnectionString)) {
var query = from order in orderContext.ShippingOrders
where order.ShipperId == shippingId
select order;
//I got a value!
shippingOrder = query.ToList().FirstOrDefault();
}
//Get the line item and nothing else.
using (OrderEntity orderContext = new OrderEntity(OrdersConnectionString)) {
var query = from orderItem in orderContext.ShippingOrderItems
where orderItem.ShipperId == shippingId
select orderItem;
//I got a value!
shippingOrderItems = query.ToList();
}
Here is where I am confused:
//Get the order *AND* the line item
using (OrderEntity orderContext = new OrderEntity(OrdersConnectionString)) {
var query = from order in orderContext.ShippingOrders.Include("ShippingOrderItems")
where order.ShipperId == shippingId
select order;
//I get a ShippingOrder result, but no items are returned. I used the SQL Server Profiler and saw the SQL that got executed; it contains the item, EF just isn't loading the object.
shippingOrder = query.ToList().FirstOrDefault();
}
I am able to get back results for a different related table. This makes me think that the EF is missing the relationship between my order and line item table, but, I'm not sure how I can fix that.
Edit: Here is the Order Entity
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="OrderModel", Name="ShippingOrder")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class ShippingOrder : EntityObject
{
#region Factory Method
/// <summary>
/// Create a new ShippingOrder object.
/// </summary>
/// <param name="shipperId">Initial value of the ShipperId property.</param>
public static ShippingOrder CreateShippingOrder(global::System.String shipperId)
{
ShippingOrder shippingOrder = new ShippingOrder();
shippingOrder.ShipperId = shipperId;
return shippingOrder;
}
#endregion
#region Primitive Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String ShipperId
{
get
{
return _ShipperId;
}
set
{
if (_ShipperId != value)
{
OnShipperIdChanging(value);
ReportPropertyChanging("ShipperId");
_ShipperId = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("ShipperId");
OnShipperIdChanged();
}
}
}
private global::System.String _ShipperId;
partial void OnShipperIdChanging(global::System.String value);
partial void OnShipperIdChanged();
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public global::System.String OrderNumber
{
get
{
return _OrderNumber;
}
set
{
OnOrderNumberChanging(value);
ReportPropertyChanging("OrderNumber");
_OrderNumber = StructuralObject.SetValidValue(value, true);
ReportPropertyChanged("OrderNumber");
OnOrderNumberChanged();
}
}
private global::System.String _OrderNumber;
partial void OnOrderNumberChanging(global::System.String value);
partial void OnOrderNumberChanged();
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public Nullable<global::System.DateTime> OrderDate
{
get
{
return _OrderDate;
}
set
{
OnOrderDateChanging(value);
ReportPropertyChanging("OrderDate");
_OrderDate = StructuralObject.SetValidValue(value);
ReportPropertyChanged("OrderDate");
OnOrderDateChanged();
}
}
private Nullable<global::System.DateTime> _OrderDate;
partial void OnOrderDateChanging(Nullable<global::System.DateTime> value);
partial void OnOrderDateChanged();
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public global::System.String CustomsComment
{
get
{
return _CustomsComment;
}
set
{
OnCustomsCommentChanging(value);
ReportPropertyChanging("CustomsComment");
_CustomsComment = StructuralObject.SetValidValue(value, true);
ReportPropertyChanged("CustomsComment");
OnCustomsCommentChanged();
}
}
private global::System.String _CustomsComment;
partial void OnCustomsCommentChanging(global::System.String value);
partial void OnCustomsCommentChanged();
#endregion
#region Navigation Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("OrderModel", "FK_ShippingOrderItem_ShippingOrder", "ShippingOrderItem")]
public EntityCollection<ShippingOrderItem> ShippingOrderItems
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<ShippingOrderItem>("OrderModel.FK_ShippingOrderItem_ShippingOrder", "ShippingOrderItem");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<ShippingOrderItem>("OrderModel.FK_ShippingOrderItem_ShippingOrder", "ShippingOrderItem", value);
}
}
}
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("OrderModel", "FK_ShippingOrderItemTracking_ShippingOrder", "ShippingOrderTracking")]
public EntityCollection<ShippingOrderTracking> ShippingOrderTrackings
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<ShippingOrderTracking>("OrderModel.FK_ShippingOrderItemTracking_ShippingOrder", "ShippingOrderTracking");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<ShippingOrderTracking>("OrderModel.FK_ShippingOrderItemTracking_ShippingOrder", "ShippingOrderTracking", value);
}
}
}
#endregion
}
I'm still not sure what the cause of the issue was. I decided to just drop my database and re-create my tables (along with the primary and foreign keys) and migrate over all the data again. It actually fixed it, but, I'm not sure what ended up being different. I was not getting any exceptions logged and based on the SQL Server Profiler it looked like the correct query was being executed.
You can also try this:
if (Order.shippingId != null && Order.shippingId > 0)
orderContext.LoadProperty(Orders, Order => Order.ShippingOrderItems);
This will fetch the matching order id in ShippingOrderItems.

Categories