I'm using mvvmlight and I find the default implementation (from code snippet of mvvminpcmsg) of an inpc is:
/// <summary>
/// The <see cref="MyProperty" /> property's name.
/// </summary>
public const string MyPropertyPropertyName = "MyProperty";
private bool _myProperty = false;
/// <summary>
/// Sets and gets the MyProperty property.
/// Changes to that property's value raise the PropertyChanged event.
/// This property's value is broadcasted by the MessengerInstance when it changes.
/// </summary>
public bool MyProperty
{
get
{
return _myProperty;
}
set
{
if (_myProperty == value)
{
return;
}
RaisePropertyChanging(MyPropertyPropertyName);
var oldValue = _myProperty;
_myProperty = value;
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
}
}
I'm wondering why the public const string is added?
public const string MyPropertyPropertyName = "MyProperty";
I don't think it's required by inpc implementation nor I see any usage of it.
So why it's added?
The public constant string for the property is added for scenarios where you're outside of the class but need to handle the property change event.
All INPC implementations need some way to resolve which property is raising the change notification. This is typically done by:
Passing in the string name of the property
Using an expression to resolve the property name
Using the CallerMemberNameAttribute attribute.
In your example, it is using a string name of the property.
Related
I have attribute [Route("api/v{version:apiVersion}/[controller]")] on every controller and this is redundant.
Is there a way to set the api/v{version:apiVersion}/ part globally somewhere so I can just specify what the controller should be?
I've tried using a base controller, MapControllerRoute, and UsePathBase.
You should be able to create an attribute that implements IRouteTemplateProvider
/// <summary>
/// Class to ensure that we use our default naming convension for controller routes
/// </summary>
public class DefaultRoutingAttribute : Attribute, IRouteTemplateProvider {
public string Template => "api/v{version:apiVersion}/[controller]";
/// <summary>
/// Order is 2 to allow explicitly overriding the default route
/// </summary>
public int? Order => 2;
public string Name { get; set; }
}
and then use it like
[ApiController]
[DefaultRouting]
public class YourController : ControllerBase {}
I have not tested exactly your route with the apiversion but it works well with a simpler route in my application.
My application is using EF6 database first approach. All the entities in the database have 2 common properties "CreatedDateTime" and "ModifiedDateTime".
Currently When i do SaveChanges() im explicitly setting these 2 properties based on if i am creating new entity or updating existing entity.
If its a new entity then set both properties else set only ModifiedDateTime property.
I wanted to know if there is a way to implicitly set these 2 properties on Save or update operation?
Update 1
I know i have to override Savechanges() method however the real issue here is SaveChanges needs to have access to these 2 properties. So i only see 2 options here:
1> Use reflection to find if entity has these properties and set it.
2> Modify default T4 generation so that it derives all entities with predefined interface. And this interface will have these 2 properties. SaveChanges() method can check if entity is derived from this interface and set the property.
I defiantly don't want to use option 1 using refection.
Is there any other way or has anyone done this before in DB first approach?
Going with your second approach: Adjust your T4 files to include a reference to an interface (e.g. IChangeTrack):
public interface IChangeTrack
{
/// <summary>
/// When has this entry be created
/// </summary>
[Required]
DateTime CreatedDateTime { get; set; }
/// <summary>
/// When has this entry been modified
/// </summary>
DateTime? ModifiedDateTime { get; set; }
}
Now overwrite your SaveChanges() routine by doing something like this:
/// <summary>
/// Enhance save changes to handle system fields.
/// </summary>
/// <returns></returns>
public override int SaveChanges()
{
HandleChanges();
int changes = base.SaveChanges();
return changes;
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
HandleChanges();
int changes = await base.SaveChangesAsync(cancellationToken);
return changes;
}
private void HandleChanges()
{
ChangeTracker.DetectChanges();
var entries = ChangeTracker.Entries<IChangeTrack>();
if (entries != null)
{
foreach (DbEntityEntry<IChangeTrack> entry in entries)
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedDateTime = DateTime.UtcNow
break;
case EntityState.Modified:
entry.Entity.ModifiedDateTime = DateTime.UtcNow;
break;
}
}
}
}
To try to isolate the source of a bug I have built the following:
SQL Server table:
CREATE TABLE [dbo].[Names]
([Key] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_Names] PRIMARY KEY CLUSTERED ([Key] ASC)
)
GO
ALTER TABLE [dbo].[Names] ADD CONSTRAINT [DF_Names_Key] DEFAULT (newid()) FOR [Key]
When I add rows to this table via the SQL Server Management Studio the guid gets a value as expected.
I then create an EF model with this table, this generated the following code:
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="Model1", Name="Names")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Names : EntityObject
{
#region Factory Method
/// <summary>
/// Create a new Names object.
/// </summary>
/// <param name="key">Initial value of the Key property.</param>
public static Names CreateNames(global::System.Guid key)
{
Names names = new Names();
names.Key = key;
return names;
}
#endregion
#region Primitive Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Guid Key
{
get
{
return _Key;
}
set
{
if (_Key != value)
{
OnKeyChanging(value);
ReportPropertyChanging("Key");
_Key = StructuralObject.SetValidValue(value);
ReportPropertyChanged("Key");
OnKeyChanged();
}
}
}
private global::System.Guid _Key;
partial void OnKeyChanging(global::System.Guid value);
partial void OnKeyChanged();
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public global::System.String Name
{
get
{
return _Name;
}
set
{
OnNameChanging(value);
ReportPropertyChanging("Name");
_Name = StructuralObject.SetValidValue(value, true);
ReportPropertyChanged("Name");
OnNameChanged();
}
}
private global::System.String _Name;
partial void OnNameChanging(global::System.String value);
partial void OnNameChanged();
I then created a test to add data to the table:
[TestMethod]
public void WriteTestMethod1()
{
using (Model1Container context = new Model1Container())
{
Names n = new Names();
n.Name = "T2";
context.Names.AddObject(n);
context.SaveChanges();
}
}
The result of this is that the row is inserted but the Guid is Empty (all zeros)
Why is this happening? Can EF work with database generated Guids?
This is old, but here is an answer anyway, since it's still relevant.
Your test code creates an instance of class Names and assigns a value to the Name property.
You don't explicitly assign anything to the Key property. The Key property is of type Guid, which is a struct (value type), not a class (reference type), so it can never be null and will always have a value, if just the default. The uninitialized default value of a Guid is all zero fields.
When you add/persist this entity instance, EF generates an INSERT to both fields, since values exist for both.
The default value expression defined in your schema is not used because a value was provided in the INSERT statement from the client side.
You can get a server generated uid value by defining the Key field as nullable, which will correspond with field type in C# of Guid? (rather than Guid). Then you can allow the Key value to remain null in your code and the server defined default expression will be used.
However, if this field is your primary key, a nullable field may not be what you need. When I'm in this situation I use a constructor that sets a Guid value (yes, on the client side) rather than allowing it to take the default value.
I think this answer is the solution to my problem but I am struggling to understand how to apply it to my problem. Like the other post, I have a two collections I want to keep in sync. My model object has collections of strings:
public class Person {
public int PersonId {get; set; }
public string PersonName { get; set; }
public List<string> PersonNicknames { get; set; }
}
I wrap this model object in its own ViewModel (PersonViewModel). To allow the Nicknames to be edited I also wrap them in their own NicknameViewModel. PersonViewModel then exposes an ObservableCollection<NicknameViewModel> NicknameViewModelCollection which is populated at construction:
foreach (string stringItem in _person.PersonNicknames)
{
var nicknameViewModel = new NicknameViewModel(stringItem);
this.NicknameViewModelCollection.Add(nicknameViewModel);
}
When a string is added, removed or changed in PersonViewModel.NicknameViewModelCollection the change is not reflected in the Model collection (i.e. Person.Nicknames). Whenever the user modifies, edits or deletes the string item I need to update the Model collection. I don't understand how the linked answer works or how to apply it to this problem. An example would be amazing... I'm just at a loss here.
This is my standard solution for what you are searching for. It has a bit of overhead for your scenario, because it works with a ViewModel type that has a field for it's context, etc. Anyway, the sollution should become obvious. The collection syncs OneWayToSource in general and TwoWay if the model collection itself is observable. Does this help you? If not, please ask...
/// <summary>
/// Observable collection of ViewModels that pushes changes to a related collection of models
/// </summary>
/// <typeparam name="TViewModel">Type of ViewModels in collection</typeparam>
/// <typeparam name="TModel">Type of models in underlying collection</typeparam>
public class VmCollection<TViewModel, TModel> : ObservableCollection<TViewModel>
where TViewModel : class, IViewModel, new()
where TModel : class
{
private readonly object _context;
private readonly ICollection<TModel> _models;
private bool _synchDisabled;
/// <summary>
/// Constructor
/// </summary>
/// <param name="models">List of models to synch with</param>
/// <param name="context"></param>
/// <param name="autoFetch">
/// Determines whether the collection of ViewModels should be
/// fetched from the model collection on construction
/// </param>
public VmCollection(ICollection<TModel> models, object context = null, bool autoFetch = true)
{
_models = models;
_context = context;
// Register change handling for synchronization
// from ViewModels to Models
CollectionChanged += ViewModelCollectionChanged;
// If model collection is observable register change
// handling for synchronization from Models to ViewModels
if (models is ObservableCollection<TModel>)
{
var observableModels = models as ObservableCollection<TModel>;
observableModels.CollectionChanged += ModelCollectionChanged;
}
// Fecth ViewModels
if (autoFetch) FetchFromModels();
}
/// <summary>
/// CollectionChanged event of the ViewModelCollection
/// </summary>
public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
/// <summary>
/// Load VM collection from model collection
/// </summary>
public void FetchFromModels()
{
// Deactivate change pushing
_synchDisabled = true;
// Clear collection
Clear();
// Create and add new VM for each model
foreach (TModel model in _models)
AddForModel(model);
// Reactivate change pushing
_synchDisabled = false;
}
private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Return if synchronization is internally disabled
if (_synchDisabled) return;
// Disable synchronization
_synchDisabled = true;
// Synchronize collection of Models
if (e.NewItems != null)
foreach (var v in e.NewItems.OfType<IViewModel<TModel>>())
v.AddModelTo(_models);
if (e.OldItems != null)
foreach (var v in e.OldItems.OfType<IViewModel<TModel>>())
v.RemoveModelFrom(_models);
//Enable synchronization
_synchDisabled = false;
}
private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_synchDisabled) return;
// Synchronize collection of ViewModels
if (e.NewItems != null)
foreach (TModel m in e.NewItems.OfType<TModel>()) this.AddIfNotNull(CreateViewModel(m));
if (e.OldItems != null) foreach (TModel m in e.OldItems) this.RemoveIfContains(GetViewModelOfModel(m));
}
private TViewModel CreateViewModel(TModel model)
{
return ViewModelCache.Get<TViewModel>.ForExistingModel(model, _context);
}
private TViewModel GetViewModelOfModel(TModel model)
{
return Items.OfType<IViewModel<TModel>>().FirstOrDefault(v => v.IsViewModelOf(model)) as TViewModel;
}
/// <summary>
/// Adds a new ViewModel for the specified Model instance
/// </summary>
/// <param name="model">Model to create ViewModel for</param>
public void AddForModel(TModel model)
{
Add(CreateViewModel(model));
}
/// <summary>
/// Adds a new ViewModel with a new model instance of the specified type,
/// which is the ModelType or derived from the Model type
/// </summary>
/// <typeparam name="TSpecificModel">Type of Model to add ViewModel for</typeparam>
public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
{
var m = new TSpecificModel();
Add(CreateViewModel(m));
}
}
I've faced with situation when I need to have EF readonly property in case of 'optimistic update'(you do not load current state of your domain object from database to check what properties are really changed. You just set your object as Modified and update it to database. You avoid redundant select and merge operations in this case).
You can't write something like this : DataContext.Entry(entity).Property(propertyName).IsModified = false;, because setting of 'false' value is not supported and you will get an exception. (in EF 4.1)
I've created a simple structure for registering readonly properties in repository.
So, you can easy Modify just nonreadonly properties.
What do you think about this?
public abstract class RepositoryBase<T> where T : class
{
private const string MethodReferenceErrorFormat = "Expression '{0}' refers to a method, not a property.";
private const string FieldReferenceErrorFormat = "Expression '{0}' refers to a field, not a property.";
protected IList<PropertyInfo> _readOnlyProperties;
/// <summary>
/// This method is used to register readonly property for Entity.
/// </summary>
/// <param name="propertyLambda">Entity property as LambdaExpression</param>
protected void RegisterReadOnlyProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda)
{
Guard.ArgumentNotNull(propertyLambda, "propertyLambda");
var propertyMember = propertyLambda.Body as MemberExpression;
if (propertyMember == null)
{
var exceptionMessage = string.Format(MethodReferenceErrorFormat, propertyLambda);
throw new ArgumentException(exceptionMessage);
}
var propertyInfo = propertyMember.Member as PropertyInfo;
if (propertyInfo == null)
{
var exceptionMessage = string.Format(FieldReferenceErrorFormat, propertyLambda);
throw new ArgumentException(exceptionMessage);
}
_readOnlyProperties.Add(propertyInfo);
}
/// <summary>
/// This method is used to attach domain object to DbContext and mark it as modified to save changes.
/// </summary>
/// <param name="entity">Detached entity</param>
public void SetModified(T entity)
{
Guard.ArgumentNotNull(entity, "entity");
//Mark whole entity as Modified, when collection of readonly properties is empty.
if(_readOnlyProperties.Count == 0)
{
DataContext.Entry(entity).State = EntityState.Modified;
return;
}
//Attach entity to DbContext.
_dbSet.Attach(entity);
//Mark all properties except readonly as Modified.
var allProperties = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var propertiesForUpdate = allProperties.Except(_readOnlyProperties);
foreach (var propertyInfo in propertiesForUpdate)
{
DataContext.Entry(entity).Property(propertyInfo.Name).IsModified = true;
}
}
This would work but I don't like the need to register modified properties directly in repository. You can forget about registered properties and code will accidentaly not save some changes - that will be bug which will be hard to find when reusing repository in complex scenarios. I like explicit definition of updated properties each time you call something like Update on your repository. Also I don't like reflection in the code. Unless you modify your code to get reflected data about each entity only once for whole application you are doing it wrong.
I wrote the answer for EFv4 but it can be easily modified to EFv4.1:
public void Update(T entity, params Expression<Func<T, object>>[] properties)
{
_dbSet.Attach(entity);
DbEntityEntry<T> entry = _context.Entry(entity);
foreach (var selector in properties)
{
entry.Property(selector).IsModified = true;
}
}
You will call it like:
repo.Update(entity, e => e.Name, e => e.Description);