I am overriding the SaveChanges() method so that I can use the ChangeTracker to get the modified properties of an entity. I need to return a Dictionary<string, string> of the modified properties so that in my controller I can call the Audit Service. So far my SaveChanges() methods looks like:
public override int SaveChanges()
{
var changeInfo = ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified)
.Select(t => new {
Original = t.OriginalValues.PropertyNames.ToDictionary(pn => pn, pn => t.OriginalValues[pn]),
Current = t.CurrentValues.PropertyNames.ToDictionary(pn => pn, pn => t.CurrentValues[pn])
});
Dictionary<string, string> modifiedProperties = new Dictionary<string, string>();
foreach (var item in changeInfo)
{
foreach (var origValue in item.Original)
{
var currValue = item.Current[origValue.Key];
if ((origValue.Value != null && currValue != null) && !origValue.Value.Equals(currValue))
{
modifiedProperties.Add(origValue.Key, string.Format("Old Value: {0}, New Value: {1}", origValue.Value, currValue));
}
}
}
return base.SaveChanges();
}
Is there a way to access the modifiedProperties dictionary in my controller so I can pass that to my service?
Controller:
if (validator.IsValid())
{
_workRequestRepo.Save(workRequest);
_auditService.Log(UserId, modelId, "Work Order", "Edit", modifiedProperties);
}
You don't have to return the modified properties, you can tackle your audit procedures inside the SaveChanges method. This is an example:
public MyContainer(IUserProvider userProvider) {
_userProvider = userProvider;
}
public override int SaveChanges() {
var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));
if (entities.Any()) {
User currentUser = _userProvider.GetCurrent();
if (currentUser == null)
throw new Exception("Current user is undefined.");
DateTime time = DateTime.Now;
foreach (var entity in entities) {
BaseEntity baseEntity = (BaseEntity)entity.Entity;
if (entity.State == EntityState.Added) {
baseEntity.Created = time;
baseEntity.CreatedBy = currentUser;
}
baseEntity.Modified = time;
baseEntity.ModifiedBy = currentUser;
// get and store the changed properties of the entity here
// ....
var changeInfo = entities.Select(t => new { Original = t.OriginalValues.PropertyNames.ToDictionary(pn => pn, pn => originalValues[pn]), Current = t.CurrentValues.PropertyNames.ToDictionary(pn => pn, pn => t.CurrentValues[pn]);
}
}
return base.SaveChanges();
}
Using IOC I would imagine you have something like:
(This is assume Auditing and not Revisioning)
Presentation:
public PersonController
{
private IPersonBL _personBL;
public PersonController(IPersonBL personBL)
{
_personBL = personBL
}
public ActionResult SavePerson(PersonVM model)
{
// if ModelState etc etc
var person = Mapper.Map<Person>(model);
_personBL.Save(person)
}
}
Business Layer
public PersonBL : IPersonBL
{
private IAuditService _auditService;
private IPersonRepo _personRepo;
public PersonBL(IAuditService auditService,
IPersonRepo personRepo)
{
_auditService = auditService;
_personRepo = personRepo;
}
public void Save(Person person)
{
PersonDTO personDTO = Mapper.Map<PersonDTO>(person);
var result = _personRepo.Save(personDTO);
if (result.Count > 0)
{
_auditService.Audit(result);
}
}
}
Data Layer
public PersonDL : IPersonDL
{
private DbContext _context;
public PersonDL(DbContext dbContext)
{
_context = dbContext;
}
public IDictionary<string, string> Save(PersonDTO person)
{
var result = new Dictionary<string, string>()
_context.Persons.Add(person);
var saveCount = _context.SaveChanges();
if (saveCount > 0)
{
// Do Object Tracking
// Populate result;
}
return result;
}
}
Related
I'm following DDD approach and have a parent and child entity with one-to-many relationship configured as shown below:
Parent Entity:
public class AnnotationEntity
{
public int Id { get; private set; }
public string Value { get; private set; }
private readonly List<AnnotationObjectEntity> _annotationObjects = new List<AnnotationObjectEntity>();
public virtual IReadOnlyList<AnnotationObjectEntity> AnnotationObjects => _annotationObjects.ToList();
protected AnnotationEntity()
{
}
private AnnotationEntity(string value) : this()
{
Value = value;
}
private void UpdateContainer(string container)
{
Value = container;
}
public void AddAnnotation(AnnotationObjectEntity annotationObject)
{
if (!_annotationObjects.Any(x => x.Id == annotationObject.Id))
{
var annotationObjectEntity = new AnnotationObjectEntity
(
annotationObject.Id,
annotationObject.PageNumber,
annotationObject.AnnotationType
);
_annotationObjects.Add(annotationObjectEntity);
return;
}
var existingAnnotation = _annotationObjects.FirstOrDefault(x => x.Id.Equals(annotationObject.Id));
if (existingAnnotation != null)
{
existingAnnotation.Update(annotationObject);
}
}
public void RemoveAnnotation(Guid id)
{
XDocument annotation = XDocument.Parse(Value);
var annotationObjectEntity = _annotationObjects.FirstOrDefault(x => x.Id == id);
if (annotationObjectEntity != null)
{
_annotationObjects.Remove(annotationObjectEntity);
annotation.Descendants("Object").Where(x => x.Element("Guid").Value == annotationObjectEntity.Id.ToString()).Remove();
}
UpdateContainer(annotation.ToString());
}
public static AnnotationEntity Create(int folderId, int documentId, string annotation)
{
XDocument doc = XDocument.Parse(annotation);
var documentAnnotations = new List<AnnotationEntity>();
var annotationEntity = new AnnotationEntity
(
annotation
);
doc.Descendants("Container").ToList().ForEach(container =>
{
container.Descendants("Object").ToList().ForEach(annotationObject =>
{
var annotationObjectEntity = new AnnotationObjectEntity
(
Guid.Parse(annotationObject.Element("Guid").Value.ToString()),
Convert.ToInt32(container.Element("PageNumber").Value.ToString()),
true
);
annotationEntity.AddAnnotation(annotationObjectEntity);
});
});
return annotationEntity;
}
public void UpdateAnnotation(string modifiedAnnotationXml)
{
var modifiedAnnotationEntity = Create(modifiedAnnotationXml);
//Removing deleted entities
var deletedAnnotationsId = _annotationObjects.Where(x => !modifiedAnnotationEntity.AnnotationObjects.Any(y => y.Id.Equals(x.Id))).Select(x => x.Id).ToList();
foreach (var annotationId in deletedAnnotationsId)
{
RemoveAnnotation(annotationId);
}
XDocument annotation = XDocument.Parse(Value);
XDocument modifiedAnnotation = XDocument.Parse(modifiedAnnotationXml);
//update or add
foreach (var annotationObject in modifiedAnnotationEntity.AnnotationObjects)
{
AddAnnotation(annotationObject);
annotation.Descendants("Object").Where(x => x.Element("Guid").Value == annotationObject.Id.ToString()).Remove();
// extract annotation object xml from modified xml and add to existing xml
var modifiedAnnotationObject = modifiedAnnotation.Descendants("Object").SingleOrDefault(x => x.Element("Guid").Value == annotationObject.Id.ToString());
annotation
.Descendants("Container").SingleOrDefault(x => x.Element("PageNumber").Value == annotationObject.PageNumber.ToString())
.Descendants("Objects").SingleOrDefault()
.Add(modifiedAnnotationObject);
UpdateContainer(annotation.ToString());
}
}
}
Child Entity:
public class AnnotationObjectEntity
{
public Guid Id { get; private set; }
public int PageNumber { get; private set; }
public bool AnnotationType { get; private set; }
public AnnotationEntity Annotation { get; }
protected AnnotationObjectEntity()
{
}
public AnnotationObjectEntity(Guid id, int pageNumber, bool annotationType) : this()
{
Id = id;
PageNumber = pageNumber;
AnnotationType = privateToSelectedUserGroup;
}
public void Update(AnnotationObjectEntity annotationObject)
{
PageNumber = annotationObject.PageNumber;
AnnotationType = annotationObject.AnnotationType;
}
}
I have configured my entities in my DbContext as shown below:
DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AnnotationEntity>(x =>
{
x.Property(y => y.Value).IsRequired();
x.HasMany(p => p.AnnotationObjects).WithOne(p => p.Annotation)
.OnDelete(DeleteBehavior.Cascade)
.Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field);
});
modelBuilder.Entity<AnnotationObjectEntity>(x =>
{
x.HasKey(k => k.Id);
x.Property(p => p.Id).HasColumnName("Id");
x.HasOne(p => p.Annotation).WithMany(p => p.AnnotationObjects);
});
}
After reading the parent entity along with child entity and if I remove/update existing child and add new child and when I try to call save to database as shown below:
Repository:
annotationEntity.UpdateAnnotation(annotationToUpdate);
_context.Annotation.Attach(annotationEntity);
//var changes = _context.ChangeTracker.Entries().Where(x => x.Entity is AnnotationObjectEntity).Select(x => (AnnotationObjectEntity)x.Entity).ToList();
//foreach (var change in changes)
//{
// _context.Entry(change).State = EntityState.Added;
//}
await _context.SaveChangesAsync();
I get the following error:
Database operation expected to affect 1 row(s) but actually affected 0
row(s). Data may have been modified or deleted since entities were
loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for
information on understanding and handling optimistic concurrency
exceptions.
Upon further analysis I found that this happens when I add new child objects to the parent object. And also I noticed that ChangeTracker has the Entity State as Modified for newly added entities. When I change it manually to Added. Then SaveChangesAsync() works.
Please assist on what I'm doing wrong and why ChangeTracker() wrongly detects the Added entity state as Modified.
I am trying to fill a list with my categories data.
My categories have cascade format. They are formatted recursively in my database.
Here is my model object.
public class CategoriesDTO
{
public int Id { get; set; }
public int Level { get; set; }
public string Name { get; set; }
public List<CategoriesDTO> Subs { get; set; }
}
So, in my business layer, i am trying to call this method like this:
DAO.Categories(0);
it will start with "Level==0" condition and then it will go on..
but i couldn't manage the Data Access Layer. I tried this:
public List<CategoriesDTO> Categories(int PrmLevel)
{
List<CategoriesDTO> DTO = new List<CategoriesDTO>();
//DB is my dbcontext.
DTO = DB.Categories.Select(x => new CategoriesDTO()
{
Id = x.Id,
Level = x.Level,
Name = x.Name
}).Where(y => y.Level == PrmLevel).ToList();
foreach (var item in DTO)
{
//im stucked
}
return DTO;
}
}
public List<CategoriesDTO> Categories(int PrmLevel)
{
List<CategoriesDTO> DTO = new List<CategoriesDTO>();
//DB is my dbcontext.
DTO = DB.Categories.Select(x => new CategoriesDTO()
{
Id = x.Id,
Level = x.Level,
Name = x.Name
}).Where(y => y.Level == PrmLevel).ToList();
foreach (var item in DTO)
{
item.Subs = ((PrmLevel + 1) <= MaxLevel) ? Categories(PrmLevel + 1) : null;
}
return DTO;
}
}
public List<CategoriesDTO> Categories(int PrmLevel)
{
List<CategoriesDTO> DTO = new List<CategoriesDTO>();
//DB is my dbcontext.
DTO = DB.Categories.Select(x => new CategoriesDTO()
{
Id = x.Id,
Level = x.Level,
Name = x.Name
}).Where(y => y.Level == PrmLevel).ToList();
foreach (var item in DTO)
{
int CountSub = 0;
CountSub = DB.Categories.Where(x => x.Level == item.Id).ToList().Count();
if (CountSub!=0)
{
item.Subs = Categories(item.Id).ToList();
}
}
return DTO;
}
}
How to add a class object to an Enitity table using LINQ???The prescriber object is built from FullMasters Entity but I need to take that object and save it to EPCS_Prescriber table. I'm using linq.
public class UserRepository : IUserRepository
{
SourceofTruthEntities context = null;
ePrescribeEntities epcs = null;
public UserRepository()
{
context = new SourceofTruthEntities();
}
public List<FullMaster> SelectByNPI(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).ToList();
return data;
}
public List<FullMaster> SelectByName(string first, string last)
{
var data = context.FullMasters.Where(x => x.FirstName == first && x.LastName == last).ToList();
return data;
}
public void SavePrescriber(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).FirstOrDefault();
Prescriber p = new Prescriber
{
First = data.FirstName,
Last = data.LastName,
DEA = data.DEA,
License = data.LIC,
Life = data.Life_Hosp,
NPI = data.NPI
};
epcs.EPCS_Prescriber.Add(p);
epcs.SaveChanges();
}
}
public void SavePrescriber(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).FirstOrDefault();
EPCS_Prescriber e = new EPCS_Prescriber();
e.FirstName = data.FirstName;
e.LastName = data.LastName;
e.DeaNo = data.DEA;
e.License = data.LIC;
e.LifeNo = data.Life_Hosp;
e.NpiNo = data.NPI;
epcs.EPCS_Prescriber.AddObject(e);
epcs.SaveChanges();
}
Both your contexts need initialization:
public UserRepository()
{
context = new SourceofTruthEntities();
ePrescribeEntities epcs = new ePrescribeEntities();
}
Assuming epcs is the db context, then you should use epcs.EPCS_Prescriber.Insert(p) rather than AddObject(p)
Unless this isn't one database, surely you just need one context here? This should work for you:
public void SavePrescriber(string id)
{
var data = context.FullMasters.Where(x => x.NPI == id).FirstOrDefault();
Prescriber p = new Prescriber
{
First = data.FirstName,
Last = data.LastName,
DEA = data.DEA,
License = data.LIC,
Life = data.Life_Hosp,
NPI = data.NPI
};
context.EPCS_Prescribers.Add(p);
context.SaveChanges();
}
Make sure your ...DbContext class contains the following:
...
public System.Data.Entity.DbSet<YourNamespace.Models.Prescriber> EPCS_Prescribers { get; set; }
...
Check the names of the classes/entities if necessary.
I'm writing a simple messaging module so one process can publish messages and another can subscribe to them. I'm using EF/SqlServer as the out of process communication mechanism. A "Server" is just a name that a publisher/subscriber pair have in common (could have been called a "Channel").
I have the following method which adds a row to the database representing a named "Server"
public void AddServer(string name)
{
if (!context.Servers.Any(c => c.Name == name))
{
context.Servers.Add(new Server { Name = name });
}
}
The problem I'm having is that when I start two clients at the same time, only one is supposed to add a new Server entry, however, that is not how it's working out. I'm actually getting the very wrong result of two entries with the same name, and realizing that an Any() guard is not sufficient for this.
The Entity for Server uses an int PK and supposedly my repository would enforce the uniqueness of the Name field. I'm starting to think this isn't going to work though.
public class Server
{
public int Id { get; set; }
public string Name { get; set; }
}
The two ways I think I could fix this both seem less than ideal:
String primary keys
Ignoring Exception
This is the issue of concurrency, right?
How can I deal with it in this situation where I want two clients to call the repository with the same Name but get a result of only one row with that name in the database?
Update: Here is the Repository Code
namespace MyBus.Data
{
public class Repository : IDisposable
{
private readonly Context context;
private readonly bool autoSave;
public delegate Chain Chain(Action<Repository> action);
public static Chain Command(Action<Repository> action)
{
using (var repo = new Data.Repository(true))
{
action(repo);
}
return new Chain(next => Command(next));
}
public Repository(bool autoSave)
{
this.autoSave = autoSave;
context = new Context();
}
public void Dispose()
{
if (autoSave)
context.SaveChanges();
context.Dispose();
}
public void AddServer(string name)
{
if (!context.Servers.Any(c => c.Name == name))
{
context.Servers.Add(new Server { Name = name });
}
}
public void AddClient(string name, bool isPublisher)
{
if (!context.Clients.Any(c => c.Name == name))
{
context.Clients.Add(new Client
{
Name = name,
ClientType = isPublisher ? ClientType.Publisher : ClientType.Subscriber
});
}
}
public void AddMessageType<T>()
{
var typeName = typeof(T).FullName;
if (!context.MessageTypes.Any(c => c.Name == typeName))
{
context.MessageTypes.Add(new MessageType { Name = typeName });
}
}
public void AddRegistration<T>(string serverName, string clientName)
{
var server = context.Servers.Single(c => c.Name == serverName);
var client = context.Clients.Single(c => c.Name == clientName);
var messageType = context.MessageTypes.Single(c => c.Name == typeof(T).FullName);
if (!context.Registrations.Any(c =>
c.ServerId == server.Id &&
c.ClientId == client.Id &&
c.MessageTypeId == messageType.Id))
{
context.Registrations.Add(new Registration
{
Client = client,
Server = server,
MessageType = messageType
});
}
}
public void AddMessage<T>(T item, out int messageId)
{
var messageType = context.MessageTypes.Single(c => c.Name == typeof(T).FullName);
var serializer = new XmlSerializer(typeof(T));
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
serializer.Serialize(sw, item);
}
var message = new Message
{
MessageType = messageType,
Created = DateTime.UtcNow,
Data = sb.ToString()
};
context.Messages.Add(message);
context.SaveChanges();
messageId = message.Id;
}
public void CreateDeliveries<T>(int messageId, string serverName, string sendingClientName, T item)
{
var messageType = typeof(T).FullName;
var query = from reg in context.Registrations
where reg.Server.Name == serverName &&
reg.Client.ClientType == ClientType.Subscriber &&
reg.MessageType.Name == messageType
select new
{
reg.ClientId
};
var senderClientId = context.Clients.Single(c => c.Name == sendingClientName).Id;
foreach (var reg in query)
{
context.Deliveries.Add(new Delivery
{
SenderClientId = senderClientId,
ReceiverClientId = reg.ClientId,
MessageId = messageId,
Updated = DateTime.UtcNow,
DeliveryStatus = DeliveryStatus.Sent
});
}
}
public List<T> GetDeliveries<T>(string serverName, string clientName, out List<int> messageIds)
{
messageIds = new List<int>();
var messages = new List<T>();
var clientId = context.Clients.Single(c => c.Name == clientName).Id;
var query = from del in context.Deliveries
where del.ReceiverClientId == clientId &&
del.DeliveryStatus == DeliveryStatus.Sent
select new
{
del.Id,
del.Message.Data
};
foreach (var item in query)
{
var serializer = new XmlSerializer(typeof(T));
using (var sr = new StringReader(item.Data))
{
var t = (T)serializer.Deserialize(sr);
messages.Add(t);
messageIds.Add(item.Id);
}
}
return messages;
}
public void ConfirmDelivery(int deliveryId)
{
using (var context = new Context())
{
context.Deliveries.First(c => c.Id == deliveryId).DeliveryStatus = DeliveryStatus.Received;
context.SaveChanges();
}
}
}
}
You could keep the int primary key, but also define a unique index on the Name column.
This way, in concurrency situations only the first insert would be successful; any subsequent clients that attempt to insert the same server name would fail with an SqlException.
I'm currently using this solution:
public void AddServer(string name)
{
if (!context.Servers.Any(c => c.Name == name))
{
context.Database.ExecuteSqlCommand(#"MERGE Servers WITH (HOLDLOCK) AS T
USING (SELECT {0} AS Name) AS S
ON T.Name = S.Name
WHEN NOT MATCHED THEN
INSERT (Name) VALUES ({0});", name);
}
}
As an exercise in thoroughness I (think I) solved this problem another way, which preserves the type safety of the EF context but adds a bit of complexity:
First, this post, I learned how to add a unique constraint to the Server table:
Here's the Context code:
public class Context : DbContext
{
public DbSet<MessageType> MessageTypes { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<Delivery> Deliveries { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<Server> Servers { get; set; }
public DbSet<Registration> Registrations { get; set; }
public class Initializer : IDatabaseInitializer<Context>
{
public void InitializeDatabase(Context context)
{
if (context.Database.Exists() && !context.Database.CompatibleWithModel(false))
context.Database.Delete();
if (!context.Database.Exists())
{
context.Database.Create();
context.Database.ExecuteSqlCommand(
#"alter table Servers
add constraint UniqueServerName unique (Name)");
}
}
}
}
Now I need a way to selectively ignore exception when saving. I did this by adding the following members to my repository:
readonly List<Func<Exception, bool>> ExceptionsIgnoredOnSave =
new List<Func<Exception, bool>>();
static readonly Func<Exception, bool> UniqueConstraintViolation =
e => e.AnyMessageContains("Violation of UNIQUE KEY constraint");
Along with a new extension method to loop keep from depending on the position of the text in the inner exception chain:
public static class Ext
{
public static bool AnyMessageContains(this Exception ex, string text)
{
while (ex != null)
{
if(ex.Message.Contains(text))
return true;
ex = ex.InnerException;
}
return false;
}
}
And I modified the Dispose method of my Repository to check if the exception should be ignored or re-thrown:
public void Dispose()
{
if (autoSave)
{
try
{
context.SaveChanges();
}
catch (Exception ex)
{
if(!ExceptionsIgnoredOnSave.Any(pass => pass(ex)))
throw;
Console.WriteLine("ignoring exception..."); // temp
}
}
context.Dispose();
}
Finally, in the method which invokes the Add, I add the acceptable condition to the list:
public void AddServer(string name)
{
ExceptionsIgnoredOnSave.Add(UniqueConstraintViolation);
if (!context.Servers.Any(c => c.Name == name))
{
var server = context.Servers.Add(new Server { Name = name });
}
}
Why wont my changes persist to db after I click save.
Product Repository:
public class ProductRepository
{
NorthwindDataContext context = new NorthwindDataContext();
public IList<EditableProduct> All()
{
return (from product in context.Products
select new EditableProduct {
ProductID = product.ProductID,
ProductName = product.ProductName,
UnitPrice = product.UnitPrice.HasValue ? product.UnitPrice.Value : default(decimal),
UnitsInStock = product.UnitsInStock.HasValue ? product.UnitsInStock.Value : default(int),
Discontinued = product.Discontinued,
LastSupply = DateTime.Today
}).ToList();
}
public EditableProduct One(Func<EditableProduct, bool> predicate)
{
return All().Where(predicate).FirstOrDefault();
}
public void Update(EditableProduct product)
{
EditableProduct target = One(p => p.ProductID == product.ProductID);
if (target != null)
{
target.ProductName = product.ProductName;
target.UnitPrice = product.UnitPrice;
target.UnitsInStock = product.UnitsInStock;
target.Discontinued = product.Discontinued;
target.LastSupply = product.LastSupply;
}
}
public void Insert(EditableProduct product)
{
product.ProductID = All().OrderByDescending(p => p.ProductID).First().ProductID + 1;
All().Insert(0, product);
}
public void Delete(EditableProduct product)
{
EditableProduct target = One(p => p.ProductID == product.ProductID);
if (target != null)
{
All().Remove(target);
}
}
}
Controller:
public partial class GridController : Controller
{
ProductRepository productRepository = new ProductRepository();
public ActionResult EditingBatch()
{
return View();
}
[HttpPost]
[GridAction]
public ActionResult _SelectBatchEditing()
{
return View(new GridModel(productRepository.All()));
}
[HttpPost]
[GridAction]
public ActionResult _SaveBatchEditing([Bind(Prefix = "inserted")]IEnumerable<EditableProduct> insertProducts,
[Bind(Prefix = "updated")]IEnumerable<EditableProduct> updatedProducts,
[Bind(Prefix = "deleted")]IEnumerable<EditableProduct> deletedProducts)
{
if (insertProducts != null)
{
foreach (var product in insertProducts)
{
productRepository.Insert(product);
}
}
if (updatedProducts != null)
{
foreach (var product in updatedProducts)
{
var target = productRepository.One(p => p.ProductID == product.ProductID);
if (target != null)
{
target.ProductName = product.ProductName;
target.UnitPrice = product.UnitPrice;
target.UnitsInStock = product.UnitsInStock;
target.LastSupply = product.LastSupply;
target.Discontinued = product.Discontinued;
productRepository.Update(target);
}
}
}
if (deletedProducts != null)
{
foreach (var product in deletedProducts)
{
productRepository.Delete(product);
}
}
return View(new GridModel(productRepository.All()));
}
}
You seem to be using the code from our sample application which indeed does not update the database. It merely updates the data in memory so every single user browsing the demos sees only his changes.
To make it work you need to update the database. The actual implementation depends on the framework you are using for data access. For example for Linq To SQL you should use the SubmitChanges method. You can check this code library project which updates the underlying database.