ViewModel trigger CollectionChanged to track ID value - xaml

I have a situation in a new WinRT app where I need to manage an ID property on a collection of objects. Essentially I'm holding the unique ID for each object which I need to increment for each new object added. This is because I'll be serializing to XML to save the data so need to manage this ID myself. If I was using SQL it would be an auto incrementing field.
The best way I could come up with was to set this using a method called from the constructor and then have a collection changed handler help me to update the value each time.
Here is the view model class:
using MM.Models;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
namespace MM.ViewModels
{
public class VehiclesViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public VehiclesViewModel()
{
Vehicles = new ObservableCollection<Vehicle>();
NewVehicle = new Vehicle();
NextVehicleID = CalculateHighestID(Vehicles.AsQueryable()) + 1;
Vehicles.CollectionChanged += new NotifyCollectionChangedEventHandler(VehicleCollectionChanged);
}
private ObservableCollection<Vehicle> _vehicles;
public ObservableCollection<Vehicle> Vehicles
{
get
{
return _vehicles;
}
set
{
if (_vehicles != value)
{
_vehicles = value;
PropertyChanged(this, new PropertyChangedEventArgs("Vehicles"));
}
}
}
void VehicleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
NextVehicleID += 1;
}
}
public Vehicle NewVehicle { get; set; }
private int _nextVehicleID;
public int NextVehicleID
{
get
{
return _nextVehicleID;
}
private set
{
_nextVehicleID = value;
PropertyChanged(this, new PropertyChangedEventArgs("NextVehicleID"));
}
}
private int CalculateHighestID(IQueryable<Vehicle> vehicles)
{
var query = vehicles.OrderByDescending(v => v.VehicleID).FirstOrDefault();
if (query != null)
{
return query.VehicleID;
}
else
{
return 1;
}
}
}
}
and here is a text button click method I added on the xaml page to add an item.
private void Button_Click_1(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
vm.Vehicles.Add(new Vehicle { VehicleID = vm.NextVehicleID });
}
However, the VehicleCollectionChanged is never called. As a test I used the same code to add a vehicle from the constructor method and that did work.
Can anyone explain why the method would not be called with adding a vehicle from the xaml button click?
Also, is there a better overall approach to keep track of an ID value for the next record?

Does your ID have to be an int (or incremental for that matter)? Could it be a GUID? At least then you could leave the creation up to the Vehicle class. As for why the event isn't being called, are you ever re-assigning the "Vehicles" property on your viewmodel? Is there a reason you have a public "set" for that property? You could potentially set a new ObservableCollection to "Vehicles" and not 1) unhook from the old event and 2) hook-up the CollectionChanged event to the new collection.

How about keeping a counter in your Vehicle class?
public class Vehicle
{
static int NextId = 1;
static object IdLock = new Object();
public int VehicleId { get; set; }
...
public Vehicle(int nextId = 0)
{
// can probably use interlocked increment instead
// of keeping a separate lock object
lock (IdLock)
{
if (nextId == 0)
{
VehicleId = NextId++;
}
else
{
NextId = nextId;
VehicleId = nextId;
}
}
}
...
}
Instead of setting NextId = 1, you may want to set it based on what is in your saved XML file. That way, it doesn't always start at 1.

Related

ASP.NET Core MVC - Is there any way to change the value of specific data on model with session?

Is there any way to change the value of specific data on model?
Like for example my model contains
item_id
item_description
quantity
and you have many records on your data model with the use of session.
on input during changing the quantity i want to update the value on of model with session?
is it recommended? or is there any better way than my approach? thank you.
What I was trying to achieve is to modify the data inside the
session. Is modifying data on session also working like a database?
where you can get the id and modify the data that you want?
From your description, I assume you are using Session to the list of records, and now you want to update the session data, right? If that is the case, you could refer to the following sample:
Refer this article to enable the session middleware in the Startup.cs file.
[Note]: Please remember to set the session expired time via the IdleTimeout property. And, since you are using session to store a list of Objects, remember to add the SessionExtensions.
//required using Microsoft.AspNetCore.Http;
//required using System.Text.Json;
public static class SessionExtensions
{
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default : JsonSerializer.Deserialize<T>(value);
}
}
Add an Item View Model:
public class ItemViewModel
{
public int ItemId { get; set; }
public string ItemDescription { get; set; }
public int Quantity { get; set; }
}
Controller: I'm using a repository to set the Initial data. After getting data from session, we could modify the data, and then, call the set method to save the latest data to the session.
private readonly ApplicationDbContext _context;
private readonly IDataRepository _repository;
public TestController(ApplicationDbContext context, IDataRepository repository)
{
_context = context;
_repository = repository;
}
private readonly string SessionKeyName = "itemlist";
public IActionResult ItemIndex()
{
List<ItemViewModel> items = new List<ItemViewModel>();
//check if the session is exist or not.
if (HttpContext.Session.Get<List<ItemViewModel>>(SessionKeyName) == default)
{ //get the initial data.
items = _repository.GetItemViewModels();
//set value to the session.
HttpContext.Session.Set<List<ItemViewModel>>(SessionKeyName, items);
}
else
{
items = HttpContext.Session.Get<List<ItemViewModel>>(SessionKeyName);
}
return View(items);
}
public IActionResult EditItem(int Id)
{
List<ItemViewModel> items = new List<ItemViewModel>();
//check if the session is exist or not.
if (HttpContext.Session.Get<List<ItemViewModel>>(SessionKeyName) == default)
{
items = _repository.GetItemViewModels();
//set value to the session.
HttpContext.Session.Set<List<ItemViewModel>>(SessionKeyName, items);
}
else
{
items = HttpContext.Session.Get<List<ItemViewModel>>(SessionKeyName);
}
return View(items.Where(c=>c.ItemId == Id).FirstOrDefault());
}
[HttpPost]
public IActionResult EditItem(ItemViewModel item)
{
List<ItemViewModel> items = new List<ItemViewModel>();
//check if the session is exist or not.
if (HttpContext.Session.Get<List<ItemViewModel>>(SessionKeyName) == default)
{
items = _repository.GetItemViewModels();
//set value to the session.
HttpContext.Session.Set<List<ItemViewModel>>(SessionKeyName, items);
}
else
{
items = HttpContext.Session.Get<List<ItemViewModel>>(SessionKeyName);
}
//based on the primary key to find the special item,
var i = items.Where(c => c.ItemId == item.ItemId).FirstOrDefault();
//update the quantity.
i.Quantity = item.Quantity;
//Update the session with the latest data.
HttpContext.Session.Set<List<ItemViewModel>>(SessionKeyName, items);
//redirect to the ItemIndex action and reload the Index page.
return RedirectToAction(nameof(ItemIndex));
}
The result like this:

EFPlus BulkInsert - How to get DB-generated IDs

Using MSSQL with IDENTITY column for IDs,
how can I get entity IDs synchronized with table IDs after calling BulkInsert?
context.BulkInsert(entities);
Neither of both achieves the requested result:
context.BulkSynchronize(entities);
context.BulkMerge(entities);
Assume we have one entity
var newSomething = new Something { Id = 0 };
and the corresponding TSQL table column definition
ID int IDENTITY(1,1)
Entity Framework automatically sets Id after calling SaveChanges()
context.SomethingSet.Add(newSomething);
context.SaveChanges();
Assert.IsTrue(newSomething.Id != 0)
See also How can I get Id of inserted entity in Entity framework?
How does EFPlus provide a way of getting the Id of inserted entities?
Disclaimer: I'm the owner of the project Entity Framework Extensions
The Entity Framework Extensions library should by default already return the ids for inserted entities.
For example, the following code should already work and return ids when using with BulkInsert.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Windows.Forms;
namespace Z.EntityFramework.Extensions.Lab
{
public partial class Form_Request_Ids : Form
{
public Form_Request_DateNull()
{
InitializeComponent();
// CLEAR
using (var ctx = new CurrentContext())
{
ctx.EntitySimples.RemoveRange(ctx.EntitySimples);
ctx.SaveChanges();
}
// TEST
using (var ctx = new CurrentContext())
{
var list = new List<EntitySimple>();
list.Add(new EntitySimple() { Id = 0, IntColumn = 1, CreatedDate = DateTime.Now });
ctx.BulkInsert(list);
}
}
public class CurrentContext : DbContext
{
public CurrentContext()
: base("CodeFirstEntities")
{
}
public DbSet<EntitySimple> EntitySimples { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Types().Configure(x => x.ToTable(GetType().DeclaringType != null ? GetType().DeclaringType.FullName.Replace(".", "_") + "_" + x.ClrType.Name : ""));
base.OnModelCreating(modelBuilder);
}
}
public class EntitySimple
{
public int Id { get; set; }
public int IntColumn { get; set; }
public DateTime CreatedDate { get; set; }
}
}
}
If you still have the issue, try to contact us directly with an example info#zzzprojects.com or post your example here.

Why is Akka.Persistance not replaying my Journal entries

I am writing an implementation of the Akka.Persistence for Service Fabric, and I don't seem to be able to get the snapshotting working. When it attempts to recover state it gets the latest snapshot but it does not replay the events since the latest snapshot. Its not clear to me If I have simply not wired up the components correctly or if my Implementation of the persistence library is incorrect.
My actor is a simple counter, my state is just the current count.
I expect that the Recover should get called first and then the Recover would get called for each journal entry between the last snapshot and the highest sequence number. There is a function ReplayMessagesAsync(...) in the journal that looks like it should do this but it does not get called.
The code for my counter is below, the rest of my code is: Code
using Akka.Actor;
using Akka.Persistence;
using Akka.Persistence.ServiceFabric.Journal;
using Akka.Persistence.ServiceFabric.Snapshot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AkkaPersistence.Actors
{
public class Counter : ReceivePersistentActor
{
public class GetCount { }
private int counter;
private CounterState State = new CounterState();
private int _msgsSinceLastSnapshot = 0;
public Counter()
{
Recover<Evt>(evt =>
{
State.Update(evt);
});
Recover<SnapshotOffer>(offer => {
var snapshotEntry = offer.Snapshot as SnapshotEntry;
if (snapshotEntry != null)
{
State = (CounterState)snapshotEntry.Snapshot;
}
});
Command<string>(str => Persist(str, s =>
{
++counter;
var evt = new Evt(s);
State.Update(evt);
if (++_msgsSinceLastSnapshot % 10 == 0)
{
//time to save a snapshot
SaveSnapshot(State.Copy());
}
}));
Command<GetCount>(get => Sender.Tell(State.Count));
Command<SaveSnapshotSuccess>(success =>
{
ServiceEventSource.Current.Message($"Saved snapshot");
DeleteMessages(success.Metadata.SequenceNr);
});
Command<SaveSnapshotFailure>(failure => {
// handle snapshot save failure...
ServiceEventSource.Current.Message($"Snapshot failure");
});
}
public override string PersistenceId
{
get
{
return "counter";
}
}
}
internal class CounterState
{
private long count = 0L;
public long Count
{
get { return count; }
set { count = value; }
}
public CounterState(long count)
{
this.Count = count;
}
public CounterState() : this(0)
{
}
public CounterState Copy()
{
return new CounterState(count);
}
public void Update(Evt evt)
{
++Count;
}
}
public class Evt
{
public Evt(string data)
{
Data = data;
}
public string Data { get; }
}
public class Cmd
{
public Cmd(string data)
{
Data = data;
}
public string Data { get; }
}
}
there were a couple of things I had wrong:
1) I needed to return what was passed in, not my SnapshotEntry which is an implementation detail of my persistence mechanism.
2) A simple miss as I translated from saving strings to attempting to save objects as part of the Journal.
3) Finally there was one more issue, that was the underlying issue, and that was, that the serialization was failing with child objects. In this piece of code I did not want to have to include the type of child object so instead I added a custom serializer (the Wire serializer) for the Journal as well as the already existing SnapshotSerializer and it is now working.

Setting internal properties in composite WF4 Activities at design time

I want to create a composite Windows Workflow Activity (under .NET 4) that contains a predefined ReceiveAndSendReply Activity. Some of the properties are predefined, but others (particularly ServiceContractName) need to be set in the designer.
I could implement this as an Activity Template (the same way ReceiveAndSendReply is implemented), but would rather not. If I later change the template, I'd have to update all previously created workflows manually. A template would also permit other developers to change properties that should be fixed.
Is there a way to do this from a Xaml Activity? I have not found a way to assign an Argument value to a property of an embedded Activity. If not, what technique would you suggest?
I haven't done this using a composite XAML activity and am getting some errors when I try but doing so through a NativeActivity is no problem. See the example code below.
public class MyReceiveAndSendReply : NativeActivity
{
private Receive _receive;
private SendReply _sendReply;
public string ServiceContractName { get; set; }
public string OperationName { get; set; }
protected override bool CanInduceIdle
{
get { return true; }
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
_receive = _receive ?? new Receive();
_sendReply = _sendReply ?? new SendReply();
_receive.CanCreateInstance = true;
metadata.AddImplementationChild(_receive);
metadata.AddImplementationChild(_sendReply);
_receive.ServiceContractName = ServiceContractName;
_receive.OperationName = OperationName;
var args = new ReceiveParametersContent();
args.Parameters["firstName"] = new OutArgument<string>();
_receive.Content = args;
_sendReply.Request = _receive;
var results = new SendParametersContent();
results.Parameters["greeting"] = new InArgument<string>("Hello there");
_sendReply.Content = results;
base.CacheMetadata(metadata);
}
protected override void Execute(NativeActivityContext context)
{
context.ScheduleActivity(_receive, ReceiveCompleted);
}
private void ReceiveCompleted(NativeActivityContext context, ActivityInstance completedInstance)
{
context.ScheduleActivity(_sendReply);
}
}

Multi-tier applications using L2S, WCF and Base Class

One day I decided to build this nice multi-tier application using L2S and WCF.
The simplified model is : DataBase->L2S->Wrapper(DTO)->Client Application.
The communication between Client and Database is achieved by using Data Transfer Objects which contain entity objects as their properties.
abstract public class BaseObject
{
public virtual IccSystem.iccObjectTypes ObjectICC_Type
{
get { return IccSystem.iccObjectTypes.unknownType; }
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_ID", AutoSync = AutoSync.OnInsert, DbType = "BigInt NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
[global::System.Runtime.Serialization.DataMemberAttribute(Order = 1)]
public virtual long ID
{
//get;
//set;
get
{
return _ID;
}
set
{
_ID = value;
}
}
}
[DataContract]
public class BaseObjectWrapper<T> where T : BaseObject
{
#region Fields
private T _DBObject;
#endregion
#region Properties
[DataMember]
public T Entity
{
get { return _DBObject; }
set { _DBObject = value; }
}
#endregion
}
Pretty simple, isn't it?. Here's the catch. Each one of the mapped classes contains ID property itself so I decided to override it like this
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Divisions")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class Division : INotifyPropertyChanging, INotifyPropertyChanged
{
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ID", AutoSync=AutoSync.OnInsert, DbType="BigInt NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=1)]
public override long ID
{
get
{
return this._ID;
}
set
{
if ((this._ID != value))
{
this.OnIDChanging(value);
this.SendPropertyChanging();
this._ID = value;
this.SendPropertyChanged("ID");
this.OnIDChanged();
}
}
}
}
Wrapper for division is pretty straightforward as well:
public class DivisionWrapper : BaseObjectWrapper<Division>
{
}
It worked pretty well as long as I kept ID values at mapped class and its BaseObject class the same(that's not very good approach, I know, but still) but then this happened:
private CentralDC _dc;
public bool UpdateDivision(ref DivisionWrapper division)
{
DivisionWrapper tempWrapper = division;
if (division.Entity == null)
{
return false;
}
try
{
Table<Division> table = _dc.Divisions;
var q = table.Where(o => o.ID == tempWrapper.Entity.ID);
if (q.Count() == 0)
{
division.Entity._errorMessage = "Unable to locate entity with id " + division.Entity.ID.ToString();
return false;
}
var realEntity = q.First();
realEntity = division.Entity;
_dc.SubmitChanges();
return true;
}
catch (Exception ex)
{
division.Entity._errorMessage = ex.Message;
return false;
}
}
When trying to enumerate over the in-memory query the following exception occurred:
Class member BaseObject.ID is unmapped.
Although I'm stating the type and overriding the ID property L2S fails to work.
Any suggestions?
Suppose I found the problem.
When writing
var q = table.Where(o => o.ID == tempWrapper.Entity.ID);
the compiler implies that the object is of BaseObject type and therefore tries to get its ID value from the BaseObject mapping and it's unmapped.
The problem seems to be resolved by explicitly declaring the type:
var q = from Division div in _dc.GetTable<Division>()
where div.ID == tempWrapper.Entity.ID
select div;