Property set inside async callback not staying set - c#

So I've got an odd bug I'm hoping someone can help me with.
I have the following code to grab some entities from WCF RIA Services, this is in Silverlight 4, though my guess is that doesn't make a difference.
What am I missing?
public class MyModel
{
...
public IEnumerable<MyEntity> Result { get; private set; }
public void Execute()
{
Context.Load(Query, LoadBehavior.RefreshCurrent, o =>
{
if (o.HasError)
{
ExecuteException = o.Error;
if (ExecuteError != null)
ExecuteError(this, EventArgs.Empty);
o.MarkErrorAsHandled();
}
else
{
//I've stepped through the code and the assignment is working
//Result != null
Result = o.Entities;
if (ExecuteSuccess != null)
ExecuteSuccess(this, EventArgs.Empty);
//Inside any Handler of ExecuteSuccess
//MyModel.Result == null
//However I set a break point after ExecuteSuccess is triggered,
//and once again MyModel.Result != null
}
if (ExecuteComplete != null)
ExecuteComplete(this, EventArgs.Empty);
ExecuteBusy = false;
}, false);
}
}
Everything works until I get to this point:
MyModel.ExecuteSuccess += (o,e) => {
//At this point MyModel.Result == null. but why?
var result = MyModel.Result;
};

Yeah I found the issue I was doing some crazy stuff in order to make MVVM + RIA Services more fun, in an inheriting class I was casting MyModel.Result to MyModel.Result as IEnumerable<TEntity>; which doesn't work. I used MyModel.Result.OfType<TEntity> instead. I'm posting all the code in case it's useful to someone else.
public abstract class RiaQuery : INotifyPropertyChanged
{
#region INotifyPropertyChanged values
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Query
private EntityQuery _Query;
public EntityQuery Query
{
get { return _Query; }
private set
{
if (_Query != value)
{
_Query = value;
RaisePropertyChanged("Query");
}
}
}
#endregion
#region Result
private IEnumerable _Result;
public IEnumerable Result
{
get { return _Result; }
private set
{
if (_Result != value)
{
_Result = value;
RaisePropertyChanged("Result");
}
}
}
#endregion
#region ExecuteBusy
private bool _ExecuteBusy;
public bool ExecuteBusy
{
get { return _ExecuteBusy; }
private set
{
if (_ExecuteBusy != value)
{
_ExecuteBusy = value;
RaisePropertyChanged("ExecuteBusy");
}
}
}
#endregion
#region ExecuteCommand
private DelegateCommand _ExecuteCommand;
public DelegateCommand ExecuteCommand
{
get
{
if (_ExecuteCommand == null)
{
_ExecuteCommand = new DelegateCommand(o => Execute(o), o => !ExecuteBusy);
}
return _ExecuteCommand;
}
}
#endregion
#region ExecuteException
private Exception _ExecuteException;
public Exception ExecuteException
{
get { return _ExecuteException; }
private set
{
if (_ExecuteException != value)
{
_ExecuteException = value;
RaisePropertyChanged("ExecuteException");
}
}
}
#endregion
public event EventHandler ExecuteBegin;
public event EventHandler ExecuteComplete;
public event EventHandler ExecuteSuccess;
public event EventHandler ExecuteError;
protected DomainContext Context;
public bool CreateQueryEachTime { get; set; }
public RiaQuery(DomainContext context)
{
if (context == null) throw new ArgumentException("context");
Context = context;
}
public void Execute(object param)
{
ExecuteBusy = true;
if (ExecuteBegin != null)
ExecuteBegin(this, EventArgs.Empty);
if (CreateQueryEachTime || Query == null)
CreateQueryInternal();
Context.Load(Query, LoadBehavior.RefreshCurrent, o =>
{
if (o.HasError)
{
ExecuteException = o.Error;
if (ExecuteError != null)
ExecuteError(this, EventArgs.Empty);
o.MarkErrorAsHandled();
}
else
{
Result = o.Entities;
if (ExecuteSuccess != null)
ExecuteSuccess(this, EventArgs.Empty);
}
if (ExecuteComplete != null)
ExecuteComplete(this, EventArgs.Empty);
ExecuteBusy = false;
}, false);
}
private void CreateQueryInternal()
{
Query = CreateQuery();
}
protected abstract EntityQuery CreateQuery();
}
public abstract class RiaQuery<TContext> : RiaQuery
where TContext : DomainContext
{
new protected TContext Context
{
get { return base.Context as TContext; }
set { base.Context = value; }
}
public RiaQuery(TContext context) : base(context) { }
}
public abstract class RiaQuery<TContext,TEntity> : RiaQuery<TContext>
where TContext : DomainContext
where TEntity : Entity
{
new public EntityQuery<TEntity> Query
{
get { return base.Query as EntityQuery<TEntity>; }
}
new public IEnumerable<TEntity> Result
{
get { return base.Result.OfType<TEntity>(); }
}
public RiaQuery(TContext context) : base(context) { }
}
public class DelegateRiaQuery<TContext> : RiaQuery<TContext>
where TContext : DomainContext
{
protected Func<TContext, EntityQuery> CreateQueryDelegate;
public DelegateRiaQuery(TContext context, Func<TContext, EntityQuery> createQueryDelegate)
: base(context)
{
if (createQueryDelegate == null) throw new ArgumentException("createQueryDelegate");
CreateQueryDelegate = createQueryDelegate;
}
protected override EntityQuery CreateQuery()
{
return CreateQueryDelegate(Context);
}
}
public class DelegateRiaQuery<TContext, TEntity> : RiaQuery<TContext, TEntity>
where TContext : DomainContext
where TEntity : Entity
{
protected Func<TContext, EntityQuery<TEntity>> CreateQueryDelegate;
public DelegateRiaQuery(TContext context, Func<TContext, EntityQuery<TEntity>> createQueryDelegate) : base(context)
{
if (createQueryDelegate == null) throw new ArgumentException("createQueryDelegate");
CreateQueryDelegate = createQueryDelegate;
}
protected override EntityQuery CreateQuery()
{
return CreateQueryDelegate(Context);
}
}
Usage looks like this:
public class MyModel : INotifyPropertyChanged
{
...
public DelegateRiaQuery<MyContxt,MyEntity> MyModelOperation { get; private set; }
public MyModel()
{
var context = new MyContext();
MyModelOperation = new DelegateRiaQuery(context, c => c.GetMyModelEntitiesQuery(this.Property1));
}
}

Related

How to copy custom ObservableCollection elements to another custom ObservableCollection?

I created a MTObservableCollection class that derives from ObservableCollection for multithreading. This is how the function looks like:
public class MTObservableCollection<T> : ObservableCollection<T> where T: LogClassa
{
public MTObservableCollection() { }
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
if (CollectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => nh.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
And here is my LogClass
public class LogClass : INotifyPropertyChanged
{
private string timestamp;
private string title;
private string message;
private MTObservableCollection<LogClass> childRowLog = new MTObservableCollection<LogClass>();
public string TimeStamp
{
get { return timestamp; }
set
{
if( timestamp != value )
{
timestamp = value;
OnPropertyChanged("TimeStamp");
}
}
}
public string Title
{
get { return title; }
set
{
if( title!= value )
{
title= value;
OnPropertyChanged("Title");
}
}
}
public string Message
{
get { return message; }
set
{
if( message!= value )
{
message= value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
So if I have two MTObservableCollection collections and I add elements to one of them such as:
public static MTObservableCollection<LogClass> parentLogCollection = new MTObservableCollection<LogClass>();
public MTObservableCollection<LogClass> childLogCollection = new MTObservableCollection<LogClass>();
childLogClass.Add( new LogClass { TimeStamp = "time", Title = "title", Message = "message" } );
Now if I want to add childLogCollection to parentLogCollection, I would do this:
parentLogCollection = new MTObservableCollection<LogClass>(childLogCollection);
I would think I need to create an implementation within the MBObservationCollection class which should be
public MTObservableCollection(MTObservableCollection<T> collection)
{
}
I just don't know what to input into it, how would I perform it?
Try changing the parent of your MTObservableCollection like this:
public MTObservableCollection(MTObservableCollection<T> collection) : base(collection)
{
}

Why in AsyncTaskManager PropertyChanged is always null?

I Try to implement sth similar to the pattern presented in this article:
http://msdn.microsoft.com/en-us/magazine/dn605875.aspx
Below is my problem description:
In my View I have set:
<Button Content="LoadData" Command="{Binding LoadDataCommand}" />
<ListBox Grid.Row="1" x:Name="listBox" ItemsSource="{Binding DataSource.Result}"
Then in codeBehind:
this.DataContext = new ProductViewModel();
Then in ProductViewModel:
public AsyncTaskManager<ObservableCollection<Product>> DataSource { get; private set; }
AND:
public ProductViewModel()
{
_loadDataCommand = new DelegateCommand(LoadDataAction);
}
private void LoadDataAction(object p)
{
ProductRepository pRepository = new ProductRepository();
DataSource = new AsyncTaskManager<ObservableCollection<Product>>(pRepository.LoadProducts());
}
And In AsyncTaskManager PropertyChanged is always null :( Why is that? What is wrong with the binding ?
But when I delete "load data" button and _loadDataCommand and just simply set
ProductRepository pRepository = new ProductRepository();
DataSource = new AsyncTaskManager<ObservableCollection<Product>>(pRepository.LoadProducts());
in the ProductViewModel constructor then it work like in the example, but I want user to have possibility to invoke load data using button not on start in constructor :/
Below is the AsyncTaskManager code:
using System;
using System.ComponentModel;
using System.Threading.Tasks;
namespace PhoneClientApp.Models
{
public sealed class AsyncTaskManager<TResult> : INotifyPropertyChanged
{
public AsyncTaskManager(Task<TResult> task)
{
Task = task;
if (!task.IsCompleted)
{
var _ = WatchTaskAsync(task);
}
}
private async Task WatchTaskAsync(Task task)
{
try
{
await task;
}
catch
{
}
var propertyChanged = PropertyChanged;
if (propertyChanged == null)
return;
propertyChanged(this, new PropertyChangedEventArgs("Status"));
propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted"));
if (task.IsCanceled)
{
propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));
}
else if (task.IsFaulted)
{
propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
propertyChanged(this, new PropertyChangedEventArgs("Exception"));
propertyChanged(this,
new PropertyChangedEventArgs("InnerException"));
propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
}
else
{
propertyChanged(this,
new PropertyChangedEventArgs("IsSuccessfullyCompleted"));
propertyChanged(this, new PropertyChangedEventArgs("Result"));
}
}
public Task<TResult> Task { get; private set; }
public TResult Result
{
get
{
return (Task.Status == TaskStatus.RanToCompletion)
? Task.Result
: default(TResult);
}
}
public TaskStatus Status
{
get { return Task.Status; }
}
public bool IsCompleted
{
get { return Task.IsCompleted; }
}
public bool IsNotCompleted
{
get { return !Task.IsCompleted; }
}
public bool IsSuccessfullyCompleted
{
get
{
return Task.Status ==
TaskStatus.RanToCompletion;
}
}
public bool IsCanceled
{
get { return Task.IsCanceled; }
}
public bool IsFaulted
{
get { return Task.IsFaulted; }
}
public AggregateException Exception
{
get { return Task.Exception; }
}
public Exception InnerException
{
get
{
return (Exception == null)
? null
: Exception.InnerException;
}
}
public string ErrorMessage
{
get
{
return (InnerException == null)
? null
: InnerException.Message;
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Please try to create a minimal reproducible set of code, and observe your Output window in the debugger for data binding errors.
The following code works just fine for me (in LINQPad):
void Main()
{
var context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
var xaml = #"<Grid><ListBox ItemsSource=""{Binding DataSource.Result}"" /></Grid>";
var element = (FrameworkElement)XamlReader.Parse(xaml, context);
element.DataContext = new ProductViewModel();
PanelManager.StackWpfElement(element);
}
class ProductViewModel
{
public ProductViewModel()
{
DataSource = new AsyncTaskManager<ObservableCollection<string>>(LoadProductsAsync());
}
private async Task<ObservableCollection<string>> LoadProductsAsync()
{
await Task.Delay(10000);
return new ObservableCollection<string> { "first", "second", "third" };
}
public AsyncTaskManager<ObservableCollection<string>> DataSource { get; private set; }
}
The list box is first shown empty, and then after a delay is populated with values.

Writing common properties to a class file in MVVM

I have properties CaretIndex and InputValue im my ViewModel which are used for Highlighting ComboBox Items which matches the text in PART_EditableTextBox of ComboBox.
So, I want them to be in a seperate file. So, I don't have to type them in each and every time I declare a new View. But problem here is that these CaretIndex and InputValue properties's setter part depends on another Class's another property named IsHighlighted and the class may change for each new collection of data.
I have a ViewModel as follows:
public class GroupsViewModel : INotifyPropertyChanged
{
public GroupsViewModel()
{
using (DBEntities db = new DBEntities())
{
GroupsAndCorrespondingEffects = (from g in db.Groups
select new GroupAndCorrespondingEffect
{
GroupName = g.Name,
CorrespondingEffect = g.Type_Effect.Name
}
).ToList();
GroupsAndCorrespondingEffects.Add
(
new GroupAndCorrespondingEffect
{
GroupName = " Primary",
CorrespondingEffect = ""
}
);
GroupsAndCorrespondingEffects = GroupsAndCorrespondingEffects.OrderBy(g => g.GroupName).ToList();
Items = (from e in db.Type_Effect
select e.Name).ToList();
}
}
public static GroupsViewModel CurrentInstance { get { return Instance; } }
private List<GroupAndCorrespondingEffect> _groupsAndCorrespondingEffects;
public List<GroupAndCorrespondingEffect> GroupsAndCorrespondingEffects
{
get
{
return _groupsAndCorrespondingEffects;
}
set
{
_groupsAndCorrespondingEffects = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");
}
}
private int _caretIndex;
public int CaretIndex
{
get { return _caretIndex; }
set
{
_caretIndex = value;
OnPropertyChanged("CaretIndex");
for (int i = 0; i < GroupsAndCorrespondingEffects.Count; i++)
{
string WordToSearch = InputValue;
if (_caretIndex != 0 && _caretIndex > 0)
{
WordToSearch = InputValue.Substring(0, _caretIndex);
}
if (WordToSearch != null)
{
GroupsAndCorrespondingEffects[i].IsHighlighted = GroupsAndCorrespondingEffects[i].GroupName.StartsWith(WordToSearch);
}
}
}
}
private string _inputValue;
public string InputValue
{
get { return _inputValue; }
set
{
_inputValue = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");
for (int i = 0; i < GroupsAndCorrespondingEffects.Count; i++)
{
string WordToSearch = _inputValue;
if (_caretIndex != 0 && _caretIndex > 0 && _caretIndex < _inputValue.Length)
{
WordToSearch = _inputValue.Substring(0, _caretIndex);
}
GroupsAndCorrespondingEffects[i].IsHighlighted = GroupsAndCorrespondingEffects[i].GroupName.StartsWith(WordToSearch);
}
}
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
The Helper Class that is used to get data in a different format (Get data in Two Columns for a comboBox.) is as below: (This class's IsHighLighted Property is referenced in the setter part of CaretIndex and InputValue.)
public class GroupAndCorrespondingEffect : INotifyPropertyChanged
{
private string _groupName;
public string GroupName
{
get
{
return _groupName;
}
set
{
_groupName = value;
OnPropertyChanged("GroupName");
}
}
private string _correspondingEffect;
public string CorrespondingEffect
{
get
{
return _correspondingEffect;
}
set
{
_correspondingEffect = value;
OnPropertyChanged("CorrespondingEffect");
}
}
private bool _isHighlighted;
public bool IsHighlighted
{
get
{
return _isHighlighted;
}
set
{
_isHighlighted = value;
OnPropertyChanged("IsHighlighted");
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
If I understand your question correctly, what you want to do is move the common bits like CaretIndex, and InputValue to an abstract base class.
Refer to below Sample code to see what I am saying:
Base Class
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private int _caretIndex;
public int CaretIndex
{
get { return _caretIndex; }
set
{
_caretIndex = value;
OnPropertyChanged("CaretIndex");
OnCaretIndexChanged();
}
}
private string _inputValue;
public string InputValue
{
get { return _inputValue; }
set
{
_inputValue = value;
OnPropertyChanged("InputValue");
OnInputValueChanged();
}
}
protected abstract void OnCaretIndexChanged();
protected abstract void OnInputValueChanged();
}
If you check the Setters for CaretIndex and InputValue, they are executing OnCaretIndexChanged and OnInputValueChanged abstract methods respectively - for which you will have implementation in the derived classes.
Now, your GroupViewModel will inherit from ViewModelBase and implement those two abstract methods.
public class GroupsViewModel : ViewModelBase
{
public GroupsViewModel()
{
using (DBEntities db = new DBEntities())
{
GroupsAndCorrespondingEffects = (from g in db.Groups
select new GroupAndCorrespondingEffect
{
GroupName = g.Name,
CorrespondingEffect = g.Type_Effect.Name
}
).ToList();
GroupsAndCorrespondingEffects.Add
(
new GroupAndCorrespondingEffect
{
GroupName = " Primary",
CorrespondingEffect = ""
}
);
GroupsAndCorrespondingEffects = GroupsAndCorrespondingEffects.OrderBy(g => g.GroupName).ToList();
Items = (from e in db.Type_Effect
select e.Name).ToList();
}
}
public static GroupsViewModel CurrentInstance { get { return Instance; } }
private List<GroupAndCorrespondingEffect> _groupsAndCorrespondingEffects;
public List<GroupAndCorrespondingEffect> GroupsAndCorrespondingEffects
{
get
{
return _groupsAndCorrespondingEffects;
}
set
{
_groupsAndCorrespondingEffects = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");
}
}
protected override void OnCaretIndexChanged()
{
for (int i = 0; i < GroupsAndCorrespondingEffects.Count; i++)
{
string wordToSearch = InputValue;
if (CaretIndex != 0 && CaretIndex > 0)
{
wordToSearch = InputValue.Substring(0, CaretIndex);
}
if (wordToSearch != null)
{
GroupsAndCorrespondingEffects[i].IsHighlighted = GroupsAndCorrespondingEffects[i].GroupName.StartsWith(wordToSearch);
}
}
}
protected override void OnInputValueChanged()
{
OnPropertyChanged("GroupsAndCorrespondingEffects");
for (int i = 0; i < GroupsAndCorrespondingEffects.Count; i++)
{
string wordToSearch = InputValue;
if (CaretIndex != 0 && CaretIndex > 0 && CaretIndex < InputValue.Length)
{
wordToSearch = InputValue.Substring(0, CaretIndex);
}
GroupsAndCorrespondingEffects[i].IsHighlighted = GroupsAndCorrespondingEffects[i].GroupName.StartsWith(wordToSearch);
}
}
}
You can follow similar approach if you want to abstract away your IsHighlighted property from class GroupAndCorrespondingEffect.
Hope this helps or gives you some ideas.
UPDATE
Added Class Diagram

On Property change took too much time and finally crash application

I am working on WPF MVVM Application.
I have a view (Employee) shown inside Main Region
this view (Employee) contain scoped regions within it where I show/display Employee details views. Its working fine till this place New, display update and delete
But I am facing strange problem with New Operation
If I create view for first time and click on new , Object got initialized my Data Object CurrentEmployee.
But If I load some previous data its been shown properly and then I click on New, my Data Object CurrentEmployee took too much time and finaly crash. the problem so far traced is in OnPropertyChange
Thanks any sort of help/suggestion is highly appriciated
whole code of view model
[Export(typeof(ITB_EMPLOYEEViewModel))]
public class TB_EMPLOYEEViewModel : ViewModelBase, ITB_EMPLOYEEViewModel
{
private TB_EMPLOYEE _currentTB_EMPLOYEE;
IRegionManager RefRegionManager;
IEventAggregator _eventAggregator;
[ImportingConstructor]
public TB_EMPLOYEEViewModel(IRegionManager regionManager, IEventAggregator eventAggregator)
: base(regionManager, eventAggregator)
{
RefRegionManager = regionManager;
HeaderInfo = "TB_EMPLOYEE";
_eventAggregator = eventAggregator;
OpenImageDialog = new DelegateCommand(OpenDialog, CanOpenDialog);
//CurrentTB_EMPLOYEE = new TB_EMPLOYEE();
//empHistoryVM = new TB_EMPLOYEE_HISTORYViewModel();
//UnLoadChild();
//LoadChild();
New();
}
private void LoadChild()
{
IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
if (regionManager.Regions.ContainsRegionWithName(RegionNames.EmployeeDetail))
{
IRegion region = regionManager.Regions[RegionNames.EmployeeDetail];
var empHistory = ServiceLocator.Current.GetInstance<uTB_EMPLOYEE_HISTORYView>();
if (region.GetView("EmployeeHistory") == null)// .Views.OfType<uTB_EMPLOYEE_HISTORYView>().SingleOrDefault() == null)
{
region.Add(empHistory, "EmployeeHistory");
}
if (CurrentTB_EMPLOYEE != null && CurrentTB_EMPLOYEE.ID!=0)
{
empHistoryVM = new TB_EMPLOYEE_HISTORYViewModel(CurrentTB_EMPLOYEE.ID);
}
else
{
empHistoryVM = new TB_EMPLOYEE_HISTORYViewModel();
}
empHistory.ViewModel = empHistoryVM;
region.Activate(region.GetView("EmployeeHistory"));
}
}
private void UnLoadChild()
{
IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
if (regionManager.Regions.ContainsRegionWithName(RegionNames.EmployeeDetail))
{
IRegion region = regionManager.Regions[RegionNames.EmployeeDetail];
if (region.GetView("EmployeeHistory") != null)// .Views.OfType<uTB_EMPLOYEE_HISTORYView>().SingleOrDefault() == null)
{
region.Remove(region.GetView("EmployeeHistory"));
}
}
}
#region DetailUserControls ViewModels
private TB_EMPLOYEE_HISTORYViewModel empHistoryVM;
#endregion DetailUserControls ViewModels
#region Commands
public DelegateCommand OpenImageDialog { get; set; }
private bool CanOpenDialog() { return true; }
public void OpenDialog()
{
using (OpenFileDialog objOpenFile = new OpenFileDialog())
{
if (objOpenFile.ShowDialog() == DialogResult.OK)
{
_currentTB_EMPLOYEE.PHOTO = Utility.ConvertImageToByte(objOpenFile.FileName);
CurrentEmployeeImage = Utility.ConvertByteToImage(_currentTB_EMPLOYEE.PHOTO);
}
}
}
public override void ShowSelectedRow()
{
if (RefRegionManager.Regions[RegionNames.MainRegion].Views.OfType<uTB_EMPLOYEEView>().SingleOrDefault() == null)
{
RefRegionManager.RegisterViewWithRegion(RegionNames.MainRegion, typeof(uTB_EMPLOYEEView));
}
RefRegionManager.RequestNavigate(RegionNames.MainRegion, "uTB_EMPLOYEEView");
UnLoadChild();
LoadChild();
}
public override void RegisterCommands()
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uTB_EMPLOYEEView>().FirstOrDefault() != null)
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uTB_EMPLOYEEView>().FirstOrDefault().ToString() == "WPFApp.View.uTB_EMPLOYEEView")
{
GlobalCommands.ShowAllCommand.RegisterCommand(ShowAllCommand);
GlobalCommands.SaveCommand.RegisterCommand(SaveCommand);
GlobalCommands.NewCommand.RegisterCommand(NewCommand);
GlobalCommands.DeleteCommand.RegisterCommand(DeleteCommand);
GlobalCommands.CloseCommand.RegisterCommand(CloseCommand);
}
}
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uListTB_EMPLOYEE>().FirstOrDefault() != null)
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uListTB_EMPLOYEE>().FirstOrDefault().ToString() == "WPFApp.ListView.uListTB_EMPLOYEE")
{
GlobalCommands.CloseCommand.RegisterCommand(CloseCommand);
GlobalCommands.SearchCommand.RegisterCommand(SearchCommand);
GlobalCommands.PrintCommand.RegisterCommand(PrintCommand);
GlobalCommands.ExportToExcelCommand.RegisterCommand(ExportToExcelCommand);
GlobalCommands.ExportToWordCommand.RegisterCommand(ExportToWordCommand);
GlobalCommands.ExportToPDFCommand.RegisterCommand(ExportToPDFCommand);
}
}
}
public override bool CanShowAll()
{
return IsActive;
}
public override void ShowAll()
{
HeaderInfo = "TB_EMPLOYEE List";
if (RefRegionManager.Regions[RegionNames.MainRegion].Views.OfType<uListTB_EMPLOYEE>().SingleOrDefault() == null)
{
RefRegionManager.RegisterViewWithRegion(RegionNames.MainRegion, typeof(uListTB_EMPLOYEE));
}
RefRegionManager.RequestNavigate(RegionNames.MainRegion, "uListTB_EMPLOYEE");
UpdateListFromDB();
}
public override void UpdateListFromDB()
{
using (DBMain objDBMain = new DBMain())
{
TB_EMPLOYEEList = objDBMain.EntTB_EMPLOYEE.ToList<TB_EMPLOYEE>();
}
}
public override void New()
{
this.CurrentTB_EMPLOYEE = new TB_EMPLOYEE();
UnLoadChild();
LoadChild();
}
public override void Close()
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uTB_EMPLOYEEView>().FirstOrDefault() != null)
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uTB_EMPLOYEEView>().FirstOrDefault().ToString() == "WPFApp.View.uTB_EMPLOYEEView")
{
RefRegionManager.Regions[RegionNames.MainRegion].Remove(RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uTB_EMPLOYEEView>().FirstOrDefault());
UnLoadChild();
}
}
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uListTB_EMPLOYEE>().FirstOrDefault() != null)
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uListTB_EMPLOYEE>().FirstOrDefault().ToString() == "WPFApp.ListView.uListTB_EMPLOYEE")
{
RefRegionManager.Regions[RegionNames.MainRegion].Remove(RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uListTB_EMPLOYEE>().FirstOrDefault());
}
}
}
public override void Delete()
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uTB_EMPLOYEEView>().FirstOrDefault() != null ||
RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uListTB_EMPLOYEE>().FirstOrDefault() != null)
{
if (CurrentTB_EMPLOYEE != null)
{
ConfirmationDialog confirmationMessage = new ConfirmationDialog("Do You want to Delete this Record of [TB_EMPLOYEE]", "Confirmation Dialog Box");
confirmationMessage.AllowsTransparency = true;
DoubleAnimation animFadeIn = new DoubleAnimation();
animFadeIn.From = 0;
animFadeIn.To = 1;
animFadeIn.Duration = new Duration(TimeSpan.FromSeconds(1));
confirmationMessage.BeginAnimation(Window.OpacityProperty, animFadeIn);
confirmationMessage.ShowDialog();
if (confirmationMessage.DialogValue)
{
if (CurrentTB_EMPLOYEE.ID != 0)
{
using (DBMain objDBMain = new DBMain())
{
objDBMain.Entry<TB_EMPLOYEE>(CurrentTB_EMPLOYEE).State = EntityState.Deleted;
objDBMain.SaveChanges();
OnPropertyChanged("CurrentTB_EMPLOYEE");
CurrentTB_EMPLOYEE = null;
}
}
else
{
CurrentTB_EMPLOYEE = null;
}
UpdateListFromDB();
}
}
}
}
public override bool CanSave()
{
return IsActive;
}
public override void Save()
{
if (RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uTB_EMPLOYEEView>().FirstOrDefault() != null ||
RefRegionManager.Regions[RegionNames.MainRegion].ActiveViews.OfType<uListTB_EMPLOYEE>().FirstOrDefault() != null)
{
if (CurrentTB_EMPLOYEE != null)
{
using (DBMain objDBMain = new DBMain())
{
objDBMain.Entry<TB_EMPLOYEE>(CurrentTB_EMPLOYEE).State = CurrentTB_EMPLOYEE.ID == 0 ? EntityState.Added : EntityState.Modified;
foreach (TB_EMPLOYEE_HISTORY obj in empHistoryVM.TB_EMPLOYEE_HISTORYList)
{
objDBMain.Entry<TB_EMPLOYEE_HISTORY>(obj).State = EntityState.Added;
CurrentTB_EMPLOYEE.TB_EMPLOYEE_HISTORYList.Add(obj);
objDBMain.SaveChanges();
}
}
UpdateListFromDB();
}
}
}
#endregion Commands
#region Properties
private ImageSource _CurrentEmployeeImage;
public ImageSource CurrentEmployeeImage { get { return _CurrentEmployeeImage; } private set { _CurrentEmployeeImage = value; OnPropertyChanged("CurrentEmployeeImage"); } }//OnPropertyChanged("CurrentTB_EMPLOYEE");
public string CurrentEmployeeImagePath { get; set; }
private List<TB_EMPLOYEE> _TB_EMPLOYEEList;
public List<TB_EMPLOYEE> TB_EMPLOYEEList
{
get { return _TB_EMPLOYEEList; }
set
{
_TB_EMPLOYEEList = value;
RaisePropertyChanged();
}
}
public TB_EMPLOYEE CurrentTB_EMPLOYEE
{
get { return _currentTB_EMPLOYEE; }
set
{
_currentTB_EMPLOYEE = value;
if (_currentTB_EMPLOYEE != null && _currentTB_EMPLOYEE.PHOTO != null)
{ CurrentEmployeeImage = Utility.ConvertByteToImage(_currentTB_EMPLOYEE.PHOTO); }
//OnPropertyChanged("CurrentTB_EMPLOYEE");
RaisePropertyChanged();
}
}
private IList<TB_SETUP_RELIGION> _listRELIGION;
public IList<TB_SETUP_RELIGION> listRELIGION
{
get
{
using (DBMain objDBMain = new DBMain())
{
_listRELIGION = objDBMain.EntTB_SETUP_RELIGION.ToList();
}
return _listRELIGION;
}
}
private IList<string> _listTitles;
public IList<string> listTitles
{
get
{
if (_listTitles == null)
{
_listTitles = new List<string>();
_listTitles.Add("Mr");
_listTitles.Add("Miss");
_listTitles.Add("Mrs");
//_listTitles.Add("Mr");
}
return _listTitles;
}
}
private IList<TB_SETUP_GENDER> _listGENDER;
public IList<TB_SETUP_GENDER> listGENDER
{
get
{
using (DBMain objDBMain = new DBMain())
{
_listGENDER = objDBMain.EntTB_SETUP_GENDER.ToList();
}
return _listGENDER;
}
}
#endregion Properties
#region FormNavigation
private bool isActive;
public override bool IsActive
{
get
{
return this.isActive;
}
set
{
isActive = value;
OnIsActiveChanged();
}
}
protected virtual void OnIsActiveChanged()
{
if (IsActive)
{
_eventAggregator.GetEvent<SubscribeToEvents>().Publish(new Dictionary<string, string>() { { "View", "TB_EMPLOYEE" }, { "FileName", #"Subscribtion to Events" } });
}
else
{
_eventAggregator.GetEvent<UnSubscribeToEvents>().Publish(new Dictionary<string, string>() { { "View", "TB_EMPLOYEE" }, { "FileName", #"UnSubscribtion to Events" } });
}
UnRegisterCommands();
RegisterCommands();
}
public override bool IsNavigationTarget(NavigationContext navigationContext)
{
this.isActive = true;
return this.isActive;
}
public override void OnNavigatedFrom(NavigationContext navigationContext)
{
UnRegisterCommands();
this.isActive = false;
}
public override void OnNavigatedTo(NavigationContext navigationContext)
{
HeaderInfo = "TB_EMPLOYEE";
this.isActive = true;
RegisterCommands();
}
#endregion FormNavigation
}
Finally solve the problem. I was using single Object CurrentTB_EMPLOYEE as current data for my form and ActiveData Item of another List view. this view model handle both form and list views. I just remove CurrentTB_EMPLOYEE from ActiveData Item to my list view and bind them through another object.. when CurrentTB_EMPLOYEE become responsible only for form its start working properly. Thanks every one for your precious time.

Equal is not defined between type Nullable<Int32> and Int32

I am writing a boring application to manage patients and their clinic history. I used SQLite combined with DbLinq libraries and DbMetal code generation utility. Here are two classes from the genereated code extracted from the underlying database:
[Table(Name="main.Patients")]
public partial class Patient : System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
{
private static System.ComponentModel.PropertyChangingEventArgs emptyChangingEventArgs = new System.ComponentModel.PropertyChangingEventArgs("");
private long _birthday;
private string _firstName;
private int _hasChildren;
private System.Nullable<int> _id;
private int _isMarried;
private string _lastName;
private string _profession;
private EntitySet<ClinicCase> _clinicCases;
private EntitySet<PatientAddress> _patientsAddresses;
private EntitySet<PatientPhoneNumber> _patientsPhoneNumbers;
#region Extensibility Method Declarations
partial void OnCreated();
partial void OnBirthdayChanged();
partial void OnBirthdayChanging(long value);
partial void OnFirstNameChanged();
partial void OnFirstNameChanging(string value);
partial void OnHasChildrenChanged();
partial void OnHasChildrenChanging(int value);
partial void OnIDChanged();
partial void OnIDChanging(System.Nullable<int> value);
partial void OnIsMarriedChanged();
partial void OnIsMarriedChanging(int value);
partial void OnLastNameChanged();
partial void OnLastNameChanging(string value);
partial void OnProfessionChanged();
partial void OnProfessionChanging(string value);
#endregion
public Patient()
{
_clinicCases = new EntitySet<ClinicCase>(new Action<ClinicCase>(this.ClinicCases_Attach), new Action<ClinicCase>(this.ClinicCases_Detach));
_patientsAddresses = new EntitySet<PatientAddress>(new Action<PatientAddress>(this.PatientsAddresses_Attach), new Action<PatientAddress>(this.PatientsAddresses_Detach));
_patientsPhoneNumbers = new EntitySet<PatientPhoneNumber>(new Action<PatientPhoneNumber>(this.PatientsPhoneNumbers_Attach), new Action<PatientPhoneNumber>(this.PatientsPhoneNumbers_Detach));
this.OnCreated();
}
[Column(Storage="_birthday", Name="Birthday", DbType="integer", AutoSync=AutoSync.Never, CanBeNull=false)]
[DebuggerNonUserCode()]
public long BirthdayBinaryDate
{
get
{
return this._birthday;
}
set
{
if ((_birthday != value))
{
this.OnBirthdayChanging(value);
this.SendPropertyChanging();
this._birthday = value;
this.SendPropertyChanged("Birthday");
this.OnBirthdayChanged();
}
}
}
[Column(Storage="_firstName", Name="FirstName", DbType="text", AutoSync=AutoSync.Never, CanBeNull=false)]
[DebuggerNonUserCode()]
public string FirstName
{
get
{
return this._firstName;
}
set
{
if (((_firstName == value)
== false))
{
this.OnFirstNameChanging(value);
this.SendPropertyChanging();
this._firstName = value;
this.SendPropertyChanged("FirstName");
this.OnFirstNameChanged();
}
}
}
[Column(Storage="_hasChildren", Name="HasChildren", DbType="integer", AutoSync=AutoSync.Never, CanBeNull=false)]
[DebuggerNonUserCode()]
public int HasChildren
{
get
{
return this._hasChildren;
}
set
{
if ((_hasChildren != value))
{
this.OnHasChildrenChanging(value);
this.SendPropertyChanging();
this._hasChildren = value;
this.SendPropertyChanged("HasChildren");
this.OnHasChildrenChanged();
}
}
}
[Column(Storage="_id", Name="ID", DbType="integer", IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
[DebuggerNonUserCode()]
public System.Nullable<int> ID
{
get
{
return this._id;
}
set
{
if ((_id != value))
{
this.OnIDChanging(value);
this.SendPropertyChanging();
this._id = value;
this.SendPropertyChanged("ID");
this.OnIDChanged();
}
}
}
[Column(Storage="_isMarried", Name="IsMarried", DbType="integer", AutoSync=AutoSync.Never, CanBeNull=false)]
[DebuggerNonUserCode()]
public int IsMarried
{
get
{
return this._isMarried;
}
set
{
if ((_isMarried != value))
{
this.OnIsMarriedChanging(value);
this.SendPropertyChanging();
this._isMarried = value;
this.SendPropertyChanged("IsMarried");
this.OnIsMarriedChanged();
}
}
}
[Column(Storage="_lastName", Name="LastName", DbType="text", AutoSync=AutoSync.Never, CanBeNull=false)]
[DebuggerNonUserCode()]
public string LastName
{
get
{
return this._lastName;
}
set
{
if (((_lastName == value)
== false))
{
this.OnLastNameChanging(value);
this.SendPropertyChanging();
this._lastName = value;
this.SendPropertyChanged("LastName");
this.OnLastNameChanged();
}
}
}
[Column(Storage="_profession", Name="Profession", DbType="text", AutoSync=AutoSync.Never)]
[DebuggerNonUserCode()]
public string Profession
{
get
{
return this._profession;
}
set
{
if (((_profession == value)
== false))
{
this.OnProfessionChanging(value);
this.SendPropertyChanging();
this._profession = value;
this.SendPropertyChanged("Profession");
this.OnProfessionChanged();
}
}
}
#region Children
[Association(Storage="_clinicCases", OtherKey="PatientID", ThisKey="ID", Name="fk_ClinicCases_0")]
[DebuggerNonUserCode()]
public EntitySet<ClinicCase> ClinicCases
{
get
{
return this._clinicCases;
}
set
{
this._clinicCases = value;
}
}
[Association(Storage="_patientsAddresses", OtherKey="PatientID", ThisKey="ID", Name="fk_PatientsAddresses_0")]
[DebuggerNonUserCode()]
public EntitySet<PatientAddress> Addresses
{
get
{
return this._patientsAddresses;
}
set
{
this._patientsAddresses = value;
}
}
[Association(Storage="_patientsPhoneNumbers", OtherKey="PatientID", ThisKey="ID", Name="fk_PatientsPhoneNumbers_0")]
[DebuggerNonUserCode()]
public EntitySet<PatientPhoneNumber> PhoneNumbers
{
get
{
return this._patientsPhoneNumbers;
}
set
{
this._patientsPhoneNumbers = value;
}
}
#endregion
public event System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void SendPropertyChanging()
{
System.ComponentModel.PropertyChangingEventHandler h = this.PropertyChanging;
if ((h != null))
{
h(this, emptyChangingEventArgs);
}
}
protected virtual void SendPropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler h = this.PropertyChanged;
if ((h != null))
{
h(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
#region Attachment handlers
private void ClinicCases_Attach(ClinicCase entity)
{
this.SendPropertyChanging();
entity.Patient = this;
}
private void ClinicCases_Detach(ClinicCase entity)
{
this.SendPropertyChanging();
entity.Patient = null;
}
private void PatientsAddresses_Attach(PatientAddress entity)
{
this.SendPropertyChanging();
entity.Patient = this;
}
private void PatientsAddresses_Detach(PatientAddress entity)
{
this.SendPropertyChanging();
entity.Patient = null;
}
private void PatientsPhoneNumbers_Attach(PatientPhoneNumber entity)
{
this.SendPropertyChanging();
entity.Patient = this;
}
private void PatientsPhoneNumbers_Detach(PatientPhoneNumber entity)
{
this.SendPropertyChanging();
entity.Patient = null;
}
#endregion
}
[Table(Name="main.PatientsAddresses")]
public partial class PatientAddress : System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
{
private static System.ComponentModel.PropertyChangingEventArgs emptyChangingEventArgs = new System.ComponentModel.PropertyChangingEventArgs("");
private string _address;
private string _domicileStatus;
private System.Nullable<int> _patientID;
private EntityRef<Patient> _patients = new EntityRef<Patient>();
#region Extensibility Method Declarations
partial void OnCreated();
partial void OnAddressChanged();
partial void OnAddressChanging(string value);
partial void OnDomicileStatusChanged();
partial void OnDomicileStatusChanging(string value);
partial void OnPatientIDChanged();
partial void OnPatientIDChanging(System.Nullable<int> value);
#endregion
public PatientAddress()
{
this.OnCreated();
}
[Column(Storage="_address", Name="Address", DbType="text", IsPrimaryKey=true, AutoSync=AutoSync.Never)]
[DebuggerNonUserCode()]
public string Address
{
get
{
return this._address;
}
set
{
if (((_address == value)
== false))
{
this.OnAddressChanging(value);
this.SendPropertyChanging();
this._address = value;
this.SendPropertyChanged("Address");
this.OnAddressChanged();
}
}
}
[Column(Storage="_domicileStatus", Name="DomicileStatus", DbType="text", AutoSync=AutoSync.Never)]
[DebuggerNonUserCode()]
public string DomicileStatus
{
get
{
return this._domicileStatus;
}
set
{
if (((_domicileStatus == value)
== false))
{
this.OnDomicileStatusChanging(value);
this.SendPropertyChanging();
this._domicileStatus = value;
this.SendPropertyChanged("DomicileStatus");
this.OnDomicileStatusChanged();
}
}
}
[Column(Storage="_patientID", Name="PatientID", DbType="integer", IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.Never)]
[DebuggerNonUserCode()]
public System.Nullable<int> PatientID
{
get
{
return this._patientID;
}
set
{
if ((_patientID != value))
{
if (_patients.HasLoadedOrAssignedValue)
{
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
this.OnPatientIDChanging(value);
this.SendPropertyChanging();
this._patientID = value;
this.SendPropertyChanged("PatientID");
this.OnPatientIDChanged();
}
}
}
#region Parents
[Association(Storage="_patients", OtherKey="ID", ThisKey="PatientID", Name="fk_PatientsAddresses_0", IsForeignKey=true)]
[DebuggerNonUserCode()]
public Patient Patient
{
get
{
return this._patients.Entity;
}
set
{
if (((this._patients.Entity == value)
== false))
{
if ((this._patients.Entity != null))
{
Patient previousPatients = this._patients.Entity;
this._patients.Entity = null;
previousPatients.Addresses.Remove(this);
}
this._patients.Entity = value;
if ((value != null))
{
value.Addresses.Add(this);
_patientID = value.ID;
}
else
{
_patientID = null;
}
}
}
}
#endregion
public event System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void SendPropertyChanging()
{
System.ComponentModel.PropertyChangingEventHandler h = this.PropertyChanging;
if ((h != null))
{
h(this, emptyChangingEventArgs);
}
}
protected virtual void SendPropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler h = this.PropertyChanged;
if ((h != null))
{
h(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
I use the following code to add an address to a patient:
PatientAddress address = new PatientAddress();
address.Address = txtAddress.Text;
address.DomicileStatus = cmbDomicileStatus.Text;
currentPatient.Addresses.Add(address);
Database.Source.PatientsAddresses.InsertOnSubmit(address);
Database.Source.SubmitChanges();
Database.Source is an instance of the class that extends DataContext in the generated code. On SubmitChanges, I receive this exception:
"Equal operator is not defined between Nullable(Of Int32) and Int32."
The message is not reported word by word, but the meaning is the same. The stack trace point to DbLinq code, more precisely to line 709 of source file DbLinq.Data.Linq.DataContext.cs. You can find the source files here: http://dblinq.codeplex.com/SourceControl/changeset/view/16800#314775 (under the body of the method SetEntityRefQueries(object entity)).
I see that the problem comes when comparing a foreign key value with a constant in an expression tree, but I couln't manage to get other information on that. Can you help me find the issue?
N.B.: the field address.PatientID (foreign key) is actually set to the correct value before the invocation of SubmitChanges.
As I mentioned in the comment above (which I'm repeating here so I can link images), your primary key should not be nullable. There should be a property in your mapping that you can change to set it, although I don't use DbLinq, so I can't give you a screenshot of it directly. Instead, here it is in the LINQ-2-SQL DBML designer (left) and the Entity Framework EDMX designer (right).
I'm not as sure about your deletion problem - that seems like it should work to me. Can you edit your question to include the whole block of your deletion code? As a preliminary guess, you're either creating a new object (instead of loading one) and then trying to delete it, or you're deleting the association without deleting the object.
As a general rule, I never delete from a database when I can avoid it - I just mark inactive. It's much easier to "undelete" that way.
Did you try: address.Patient = currentPatient
instead of: currentPatient.Addresses.Add(address)?
PatientAddress address = new PatientAddress();
address.Address = txtAddress.Text;
address.DomicileStatus = cmbDomicileStatus.Text;
address.Patient = currentPatient;
Database.Source.PatientsAddresses.InsertOnSubmit(address);
Database.Source.SubmitChanges();

Categories