using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.Core.Commands;
using Tango.BL.Entities;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.DB.Managers;
using Tango.SharedUI;
using System.Data.Entity.Infrastructure;
using Tango.MachineStudio.DB.Messages;
using System.Collections.ObjectModel;
using System.Reflection;
using Tango.MachineStudio.Common.StudioApplication;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using Tango.BL;
using Tango.Core.DI;
using Tango.Core.ExtensionMethods;
namespace Tango.MachineStudio.DB.ViewModels
{
public abstract class DbTableViewModel<T> : ViewModel where T : class, IObservableEntity
{
protected INotificationProvider _notification;
/// <summary>
/// Initializes a new instance of the <see cref="DbTableViewModel"/> class.
/// </summary>
public DbTableViewModel(INotificationProvider notification) : base()
{
_notification = notification;
Adapter = ObservablesEntitiesAdapter.Instance;
ValidationErrors = new ObservableCollection<string>();
AddCommand = new RelayCommand(OnAdd);
EditCommand = new RelayCommand(OnEdit, (x) => SelectedEntity != null);
DeleteCommand = new RelayCommand(OnDelete, (x) => SelectedEntity != null);
DialogOKCommand = new RelayCommand(() => OnDialogOKPressed(DialogOpenMode, EditEntity));
DialogCancelCommand = new RelayCommand(() => OnDialogCancelPressed(DialogOpenMode, EditEntity));
IsDialogOpen = false;
}
private T _editEntity;
/// <summary>
/// Gets or sets the edit entity.
/// </summary>
public T EditEntity
{
get { return _editEntity; }
set { _editEntity = value; RaisePropertyChangedAuto(); }
}
private DialogOpenMode _dialogOpenMode;
/// <summary>
/// Gets or sets the dialog open mode.
/// </summary>
public DialogOpenMode DialogOpenMode
{
get { return _dialogOpenMode; }
set { _dialogOpenMode = value; RaisePropertyChangedAuto(); }
}
private bool _isDialogOpen;
/// <summary>
/// Gets or sets a value indicating whether this instance is dialog open.
/// </summary>
public bool IsDialogOpen
{
get { return _isDialogOpen; }
set { _isDialogOpen = value; RaisePropertyChangedAuto(); }
}
private ObservablesEntitiesAdapter _adapter;
/// <summary>
/// Gets or sets the DB adapter.
/// </summary>
public ObservablesEntitiesAdapter Adapter
{
get { return _adapter; }
set { _adapter = value; RaisePropertyChangedAuto(); }
}
private T _selectedEntity;
/// <summary>
/// Gets or sets the selected entity.
/// </summary>
public T SelectedEntity
{
get { return _selectedEntity; }
set { _selectedEntity = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
}
private String _filter;
/// <summary>
/// Gets or sets the search filter.
/// </summary>
public String Filter
{
get { return _filter; }
set { _filter = value; RaisePropertyChangedAuto(); OnFilterChanged(value); }
}
/// <summary>
/// Gets or sets the dialog OK command.
/// </summary>
public RelayCommand DialogOKCommand { get; set; }
/// <summary>
/// Gets or sets the dialog cancel command.
/// </summary>
public RelayCommand DialogCancelCommand { get; set; }
/// <summary>
/// Gets or sets the add command.
/// </summary>
public RelayCommand AddCommand { get; set; }
/// <summary>
/// Gets or sets the edit command.
/// </summary>
public RelayCommand EditCommand { get; set; }
/// <summary>
/// Gets or sets the delete command.
/// </summary>
public RelayCommand DeleteCommand { get; set; }
protected override void OnValidating()
{
base.OnValidating();
ValidationErrors.Clear();
foreach (var prop in typeof(T).GetPropertiesWithAttribute<ColumnAttribute>(BindingFlags.Public | BindingFlags.Instance).Where(x => !x.PropertyType.IsGenericType && x.PropertyType.IsClass && !x.Name.Contains("Guid")))
{
if (prop.GetValue(EditEntity) == null)
{
ValidationErrors.Add(prop.Name + " is required");
}
}
}
/// <summary>
/// Called when delete command invoked.
/// </summary>
protected virtual async void OnDelete()
{
using (_notification.PushTaskItem("Saving changes to database..."))
{
try
{
SelectedEntity.Detach(Adapter.Context);
await Adapter.Context.SaveChangesAsync();
}
catch (Exception ex)
{
Adapter.Invalidate();
_notification.ShowError("Could not delete entity." + Environment.NewLine + ex.Message);
}
SelectedEntity = null;
}
}
/// <summary>
/// Called when edit command invoked.
/// </summary>
protected virtual void OnEdit()
{
ValidationErrors.Clear();
DialogOpenMode = DialogOpenMode.Editing;
EditEntity = GetEditableEntity(DialogOpenMode);
TangoMessenger.Default.Send(new OpenEntityEditViewMessage(DialogOpenMode, this, typeof(T)));
IsDialogOpen = true;
}
/// <summary>
/// Called when add command invoked.
/// </summary>
protected virtual void OnAdd()
{
ValidationErrors.Clear();
DialogOpenMode = DialogOpenMode.Adding;
EditEntity = GetEditableEntity(DialogOpenMode);
var codeProp = EditEntity.GetType().GetProperty("Code");
if (codeProp != null)
{
if (EditEntity.GetDbSet<T>(Adapter.Context).Count() > 0)
{
int lastCode = EditEntity.GetDbSet<T>(Adapter.Context).ToList().Max(x => (int)codeProp.GetValue(x));
codeProp.SetValue(EditEntity, lastCode + 1);
}
}
TangoMessenger.Default.Send(new OpenEntityEditViewMessage(DialogOpenMode, this, typeof(T)));
IsDialogOpen = true;
}
/// <summary>
/// Called when dialog closes with OK button.
/// </summary>
/// <param name="mode">The mode.</param>
protected virtual async void OnDialogOKPressed(DialogOpenMode mode, T entity)
{
if (!Validate()) return;
if (ValidationErrors.Count > 0) return;
var codeProp = entity.GetType().GetProperty("Code");
if (codeProp != null)
{
int code = (int)codeProp.GetValue(entity);
if (entity.GetDbSet<T>(Adapter.Context).ToList().Where(x => x.Guid != entity.Guid).Select(x => (int)codeProp.GetValue(x)).Contains(code))
{
_notification.ShowError("The entity code specified already exists.");
return;
}
}
TangoMessenger.Default.Send(new CloseEntityEditViewMessage());
if (mode == DialogOpenMode.Editing)
{
entity.ShallowCopyTo(SelectedEntity);
entity = SelectedEntity;
}
OnBeforeEntitySave(mode, entity);
using (_notification.PushTaskItem("Saving changes to database..."))
{
if (mode == DialogOpenMode.Adding)
{
entity.Attach(Adapter.Context);
}
try
{
await entity.SaveAsync(Adapter.Context);
}
catch (DbUpdateException ex)
{
if (mode == DialogOpenMode.Adding)
{
entity.Detach(Adapter.Context);
}
Adapter.Invalidate();
_notification.ShowError("Could not save entity." + Environment.NewLine + ex.InnerException.InnerException != null ? ex.InnerException.InnerException.Message : ex.InnerException.Message);
}
catch (Exception)
{
Adapter.Invalidate();
_notification.ShowError("Could not save entity." + Environment.NewLine + "Please make sure all fields are properly populated.");
}
IsDialogOpen = false;
SelectedEntity = EditEntity;
SelectedEntity = null;
}
}
/// <summary>
/// Called when [before entity save].
/// </summary>
/// <param name="mode">The mode.</param>
/// <param name="entity">The entity.</param>
protected virtual void OnBeforeEntitySave(DialogOpenMode mode, T entity)
{
}
/// <summary>
/// Called when dialog closes with cancel button.
/// </summary>
/// <param name="mode">The mode.</param>
protected virtual void OnDialogCancelPressed(DialogOpenMode mode, T entity)
{
TangoMessenger.Default.Send(new CloseEntityEditViewMessage());
IsDialogOpen = false;
}
/// <summary>
/// Gets the editable entity.
/// </summary>
/// <param name="mode">The mode.</param>
/// <returns></returns>
private T GetEditableEntity(DialogOpenMode mode)
{
if (mode == DialogOpenMode.Adding)
{
var newEntity = Activator.CreateInstance<T>();
InitializeEntity(newEntity);
return newEntity;
}
else
{
return SelectedEntity.ShallowClone();
}
}
protected virtual void OnFilterChanged(String filter)
{
String viewSourceName = this.GetType().Name.Replace("ViewVM", "ViewSource");
ICollectionView collectionView = Adapter.GetType().GetProperty(viewSourceName).GetValue(Adapter) as ICollectionView;
collectionView.Filter = (entity) =>
{
return FilterEntity((T)entity, filter);
};
}
private bool FilterEntity(T entity, String filter)
{
foreach (var prop in entity.GetType().GetPropertiesWithAttribute<ColumnAttribute>(BindingFlags.Public | BindingFlags.Instance).Where(x => x.PropertyType.IsClass && x.PropertyType != typeof(String) && x.PropertyType != typeof(byte[]) && !x.PropertyType.IsGenericType))
{
object obj = prop.GetValue(entity);
if (obj != null)
{
foreach (var innerProp in obj.GetType().GetPropertiesWithAttribute<ColumnAttribute>(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name != "Deleted" && x.Name != "ID" && x.Name != "LastUpdated").Where(x => !x.PropertyType.IsGenericType && (x.PropertyType.IsClass || x.PropertyType == typeof(String))))
{
object value = innerProp.GetValue(obj);
if (value != null)
{
if (value.ToString().ToLower().Contains(filter.ToLower()))
{
return true;
}
}
}
}
}
return
entity.
GetType().
GetPropertiesWithAttribute<ColumnAttribute>(BindingFlags.Public | BindingFlags.Instance).
Where(x => x.Name != "Deleted" && x.Name != "ID" && x.Name != "LastUpdated").
Where(x => !x.PropertyType.IsGenericType && (x.PropertyType.IsClass || x.PropertyType == typeof(String))).
Select(prop => prop.GetValue(entity).ToString()).
ToList().
Any(x => x.ToLower().Contains(filter.ToLower()));
}
protected virtual void InitializeEntity(T entity)
{
}
}
}