We have a repository for CRUD operations. Basically if we want to do any of the read/write operations we can do something like
using (MemberRepo mRepo = new MemberRepo())
{
mRepo.UpdateEntity(member);
mRepo.Save();
}
What I need to know however is if it is possible to know a certain column value in the database before we update it? For example if there is a field in the member table called Status and I want to know the current existing status of that member before I update it with some other value?
Thanks.
The following code will let you set the creation date of any entity that has this column/attribute. It can work as a trigger in some cases.
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("CreationDate") != null))
{
if (entry.State == EntityState.Added)
{
entry.Property("CreationDate").CurrentValue = DateTime.Now;
}
}
return base.SaveChanges();
}
Related
Please excuse my limited knowledge of Entity Framework/C#. I'm sure there is an easy way to do what I wish to do.
I want to iterate through an object that I received from the database in order to check which fields are different from my update DTO. I wish to do this so only the changed fields are updated in the objFromDb. I am aware that if I only change the fields in the objFromDb that are modified, EF will only update the changed fields. I'm not sure if this is necessary or if there is a better way to do it.
Here is my update code (works but not the way I want it to) with the individual properties as well as the commented code that I was trying to accomplish. I don't like hard-coding the individual properties as this will require maintenance in the event that the object is changed.
public T_IFS_EmployeeDTO Update(T_IFS_EmployeeDTO ojbDTO)
{
var objFromDb = _db.T_IFS_Employee.FirstOrDefault(u => u.EmployeeID == ojbDTO.EmployeeID);
if (objFromDb != null)
{
//foreach (var prop in objFromDb.GetType().GetProperties(BindingFlags.Instance))
//{
// if (prop.GetValue(objFromDb) != prop.GetValue(ojbDTO))
// {
// objFromDb.prop.GetValue(objFromDb) = prop.GetValue(ojbDTO);
// }
//}
if (objFromDb.FirstName != ojbDTO.FirstName) objFromDb.FirstName = ojbDTO.FirstName;
if (objFromDb.LastName != ojbDTO.LastName) objFromDb.LastName = ojbDTO.LastName;
if (objFromDb.UserName != ojbDTO.UserName) objFromDb.UserName = ojbDTO.UserName;
if (objFromDb.Password != ojbDTO.Password) objFromDb.Password = ojbDTO.Password;
if (objFromDb.AccessLevel != ojbDTO.AccessLevel) objFromDb.AccessLevel = ojbDTO.AccessLevel;
_db.T_IFS_Employee.Update(objFromDb);
_db.SaveChanges();
return _mapper.Map<T_IFS_Employee, T_IFS_EmployeeDTO>(objFromDb);
}
return ojbDTO;
I'm sure there is an easy way to do this but I haven't been able to figure it out. I do appreciate your time.
-Edit-
I think the following will work but will EF know if a field has not been modified and is it possible that it is as simple as this:
var objFromDb = _db.T_IFS_Employee.FirstOrDefault(u => u.EmployeeID == ojbDTO.EmployeeID);
var objFromCall = _mapper.Map<T_IFS_EmployeeDTO, T_IFS_Employee>(ojbDTO);
if (objFromDb != null)
{
objFromDb = objFromCall;
Entity Framework Core will check the values of your entity and check them against a snapshot of what they were like when you loaded them. See this for the details.
So you should be able to do:
var objFromDb = _db.T_IFS_Employee.FirstOrDefault(u => u.EmployeeID == ojbDTO.EmployeeID);
if (objFromDb != null)
{
_mapper.Map<T_IFS_EmployeeDTO, T_IFS_Employee>(ojbDTO, objFromDb);
//This overload of .Map sets properties in an existing object, as
//opposed to creating a new one
}
This will overwrite all properties in objFromDb with values from objDTO, But only the ones that are different will be written to the database when you call SaveChanges().
Setting objFromDb to objFromCall will overwrite your reference from the database and this won't be tracked at all.
And there's no need to call .Update() if you received the object from the DbContext and you haven't disabled change tracking.
I am trying to solve a problem as well as learn and improve my code skills here.
I am using Entity Framework and am tasked with writing to a SQL table, where I can update or insert based on whether a row exists or not. If it doesn't exist then add it, if it does exist then update it if required.
I have 2 lists, the first list is the EF type from the table that I am writing to. The second list is a class which is made up from a SQL query but shares some of the columns from the table which needs updating, thus if they differ then update the table with the differing property values.
foreach (var tbl in Table_List)
{
foreach (var query in SQL_Query)
{
if (tbl.ID == query.ID)
{
bool changed = false;
if (tbl.Prop1 != query.Prop1)
{
tbl.Prop1 = query.Prop1;
changed = true;
}
if (tbl.Prop2 != query.Prop2)
{
tbl.Prop2 = query.Prop2;
changed = true;
}
if (changed)
await Context.SaveChangesAsync();
}
}
}
There are 10 properties in total in the class, but even if all 10 of them differ I only have to update 2 properties, the rest can stay the same. So to summarize, my question is;
Is there a better way to update these 2 properties? Something other than a bulky series of if statements and foreach loops? Any info on straight up inserting would be appreciated too, thanks very much!
EF uses an internal ChangeTracker. This means that when you change a property of an entity that is being tracked (you queried the lists using a DbSet on the Context) it will marked as "Changed" in the ChangeTracker. This is used by the SaveChangesAsync to determine what to do, ie. insert of update and what fields need to be updated.
Second, this ChangeTracker is smart enough to detect that when you set a property to the same value it already has it won't be marked as a Change.
Also with this ChangeTracker there is no need to call SaveChangesAsync after every change. You can call it at the end of the loop.
foreach (var tbl in Table_List)
{
foreach (var query in SQL_Query)
{
if (tbl.ID == query.ID)
{
tbl.Prop1 = query.Prop1;
tbl.Prop2 = query.Prop2;
}
}
}
await Context.SaveChangesAsync();
I have a Comment table which can be linked to many different entities that have comments, but for reasons, I have not linked those tables. Instead Comment contains TableReferenceId and EntryReferenceId. TableReferenceId is just an int that we can check in the app layer as to which entity/table that comment refers to, and EntryReferenceId is an int that refers to a particular entry in said entity/table to which the comment belongs.
Querying such comments by table and entry reference would be fine, but when inserting bulk data, I am drawing a blank. For example if I have Vehicle entity and a Vehicle can have many comments, when inserting the data, how would I link them since I don't have a VehicleId yet? Is this doable or is it better to just go many-to-many route for each of the tables that link to comments?
If you can avoid this situation, then you should try to, or you should try to avoid supporting a bulk insert. If you must do this though, then either of the following patterns may work for you.
Perform the Bulk Insert in 2 stages, before the normal import, maintain a map or dictionary of records and the comments that they are linked to, then after the first call to SaveChanges() the IDs will be available to insert.
You could store the mapped comments inside an unbound collection on the entity, after SaveChanges() if there are any entries in this collection, they should be inserted using the new record's Id.
Lets look at the first option:
var mappedComments = new Dictionary<Vehicle,Comment[]>();
// bulk processing, however you choose to do it
// importantly for each item, capture the record reference and the comments
foreach(var item in source)
{
Vehicle newItem;
... construct/parse the new Entity object
List<Comment> newComments = new List<Comment>();
... parse the comments records
// store the map
mappedComments.Add(newItem, newComments.ToArray());
// Add the entity to the context?
db.AddToVehicles(newItem);
}
db.SaveChanges();
foreach(var mapEntry in mappedComments)
{
var newVehicle = mapEntry.Key;
// replace this with your actual logic of course...
int vehicleTableReferenceId = db.TableReferences.Single(x => x.TableName == nameof(Vehicle));
foreach(var comment in mappEntry.Value)
{
comment.TableReferenceId = vehicleTableReferenceId;
comment.EntityReferenceId = newVehicle.Id; // the Id that is now populated
db.AddToComments(comment);
}
}
db.SaveChanges();
If you have a lot Entity types that exhibit this linking behaviour, then you could build this functionality into the Entities themselves, by embedding the mapped comments within the entity itself.
Define an Interface that describes an object that has a weak reference to these Comments
public interface ICommentsToInsert
{
// Only necessary if your convention is NOT to use a common name for the PK
int Id { get; }
ICollection<Comment> CommentsToInsert { get;set;}
}
Implement this interface and add an unmapped collection property to the entities to store the Comment Entries to insert against each record.
partial class Vehicle : ICommentsToInsert
{
[NotMapped]
int ICommentsToInsert.Id { get => Vehicle_Id; }
[NotMapped]
public ICollection<Comment> CommentsToInsert { get;set; } = new HashSet<Comment>();
}
In your bulk logic, add the Comment records into the Vehicle.CommentsToInsert collection, I'll leave that to you...
Override SaveChanges() to detect entities that have comments and re-process them after the save operation.
In this example I am storing the EntityState for all modified entries before the save, this is overkill for this particular example, but you only lose this state information during the save, keeping a record of it becomes useful for a whole range of other applications for post-processing logic.
public override int SaveChanges()
{
var beforeStates = BeforeSaveChanges();
int result = base.SaveChanges();
if (AfterSaveChanges(beforeStates);
result += base.SaveChanges();
return results;
}
private Dictionary<DbEntityEntry, EntityState> BeforeSaveChanges()
{
var beforeSaveChanges = new Dictionary<DbEntityEntry, EntityState>();
foreach( var entry in this.ChangeTracker.Entries())
{
//skip unchanged entries!
if (entry.State == EntityState.Unchanged)
continue;
// Today, only cache the ICommentsToInsert records...
if (entry.Entity is ICommentsToInsert)
beforeSaveChanges.Add(entry, entry.State);
}
return beforeSaveChanges;
}
private bool AfterSaveChanges(Dictionary<DbEntityEntry, EntityState> statesBeforeSaveChanges)
{
bool moreChanges = false;
foreach (var entry in statesBeforeChanges)
{
if (entry.Key.Entity is ICommentsToInsert hasComments)
{
if(hasComments.CommentsToInsert.Any())
{
moreChanges = true;
// Get the Id to the TableReference, based on the name of the Entity type
// you would normally cache this type of lookup, rather than hitting the DB every time
int tableReferenceId = db.TableReferences.Single(x =
> x.TableName == entry.Key.Entity.GetType().Name);
foreach (var comment in hasComments.CommentsToInsert)
{
comment.TableReferenceId = tableReferenceId;
comment.EntityReferenceId = hasComments.Id;
db.AddToComments(comment);
}
}
}
}
return moreChanges;
}
You can further evolve this by implementing DbTransaction scopes to rollback the whole lot if things fail, this code itself is para-phrased from my common routines that I use in production code, so whilst it may not work as is, the concept has served me well in many projects.
I'm using Entity Framework 6, Code First approach. I'll try to present my problem with a simple piece of code:
public void ViewEntity(MyEntity Entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
Db.MyEntities.Attach(Entity); // Exception
}
}
The exception message is: Attaching an entity of type 'MyProgram.MyEntity' failed because another entity of the same type already has the same primary key value.
From what I've read on MSDN it's an expected behaviour. But what I want on that last line is to first check if there is an entity with the same key already attached to a context; if it is, use it instead, and only otherwise attach my entity to context.
But I've failed to find a way to do so. There are many utility methods on ObjectContext instance (for example GetObjectByKey). I can't test them all 'cause they all ultimately need a qualifiedEntitySetName, and I don't have any in my real imlpementation, because this method should be on an abstract class and it should work for all entity types. Calling Db.Entity(this) is no use, there is no EntityKey which would have EntitySetName.
So all of this became complex really fast. And in my terms I just want to check if the object is already in "cache" (context), use it, otherwise use my object and attach it to this context.
To be clear, I have a detached object from a TreeNode.Tag in the first place, and I just want to use it again, or if it's impossible; if there already is one in the context), use that one instead. Maybe I'm missing some crucial concepts of EF6, I'm just starting out with EF.
I've found a solution for me. As I guessed correctly ObjectContext.GetObjectByKey method does what I need, but first I needed to construct qualifiedEntitySetName, and I found a way to do so. A tad bit cumbersome (using reflection, iterating properties of MyDbContext), but does not compare to a headache of a problem I made out of all this. Just in case, here's the patch of code that is a solution for me:
public SdsAbstractObject GetAttachedToContext()
{
var ObjContext = (SdsDbContext.Current as IObjectContextAdapter).ObjectContext;
var ExistingItem = ObjContext.GetObjectByKey(GetEntityKey()) as SdsAbstractObject;
if (ExistingItem != null)
return ExistingItem;
else
{
DbSet.Attach(this);
return this;
}
}
public EntityKey GetEntityKey()
{
string DbSetName = "";
foreach (var Prop in typeof(SdsDbContext).GetProperties())
{
if (Prop.PropertyType.IsGenericType
&& Prop.PropertyType.GenericTypeArguments[0] == ObjectContext.GetObjectType(GetType()))
DbSetName = Prop.Name;
}
if (String.IsNullOrWhiteSpace(DbSetName))
return null;
else
return new EntityKey("SdsDbContext." + DbSetName, "Id", Id);
}
An Entity can be in one of five stages : Added, Unchanged, Modified, Deleted, Detached.
public void ViewEntity(MyEntity entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
// Set the Modified state of entity or you can write defensive code
// to check it before set the state.
if (Db.Entry(entity).State == EntityState.Modified) {
Db.Entry(entity).State = EntityState.Modified
}
// Attached it
Db.MyEntities.Attach(Entity);
Db.SaveChanges();
}
}
Since EF doesn't know which properties are different from those in the database, it will update them all.
I want to update only some columns say N-1 columns in a table containing N columns using NHibernate QueryOver syntax.
The query I tried is something like this.
public T UpdatePost(Object DateUpdated, object DateExpires, object Id)
{
using (var session=sessionFactory.OpenSession())
{
using (var transaction=session.BeginTransaction())
{
session.Update(DateUpdated, Id);
session.Update(DateExpires, Id);
transaction.Commit();
return session.Get<T>(Id);
}
}
}
Calling method is
obj.UpdatePost(DateTime.Now, DateTime.Now.AddDays(30), 3);
Error is
There is a problem with your mappings. You are probably trying to map a System.ValueType to a which NHibernate does not allow or you are incorrectly using the IDictionary that is mapped to a . A ValueType (System.DateTime) can not be used with IdentityKey.
How to achieve this?
Your UpdatePost method makes no sense. NHibernate's session.Update expects an entity object that should be updated. Documentation for the overload you are trying to use is:
Updates the persistent state associated with the given identifier.
First argument should be an entire entity object.
If you try to analyze your code, there's no way for NHibernate to know which entity do you want to update. Update method is not generic, neither is the Session. You are just trying to give it a date value and an id. How would NHibernate know which table and which column to update?
In order to do partial updates with NHibernate, you would need to use HQL update queries (DML-style operations).
Here's how it would look like in your case:
public T UpdatePost(Object DateUpdated, object DateExpires, object Id)
{
using (var session=sessionFactory.OpenSession())
{
using (var transaction=session.BeginTransaction())
{
string hqlUpdate =
"update Post p set p.DateUpdated = :dateUpdated, p.DateExpires = :dateExpires where p.id = :id";
session.CreateQuery(hqlUpdate)
.SetDateTime("dateUpdated", DateUpdated)
.SetDateTime("dateExpires", DateExpires)
.SetParameter("id", Id)
.ExecuteUpdate();
transaction.Commit();
return session.Get<T>(Id);
}
}
}
On a side note, since you are already getting the entity itself after the update, you could simply load the entity first, change its properties and save it. You would still have two database operations.
public T UpdatePost(DateTime DateUpdated, DateTime DateExpires, object Id)
{
using (var session=sessionFactory.OpenSession())
{
using (var transaction=session.BeginTransaction())
{
T post = session.Get<T>(Id);
post.DateUpdated = DateUpdated;
post.DateExpires = DateExpires;
session.Update(post);
transaction.Commit();
return post;
}
}
}
If you really want to force NHibernate to update only the columns that are changed, you can specify dynamic-update="true" in class mapping declaration.
dynamic-update (optional, defaults to false): Specifies that UPDATE SQL should be generated at runtime and contain only those columns whose values have changed.
This will be the optimal solution as SetDateTime accepts only DateTime types as its parameter values. and while declaring this method generically, we must format the query according to its entity type.
public T UpdatePost(DateTime DateUpdated, DateTime DateExpires, object Id) <-- DateTime parameters
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
string hqlUpdate = string.Format(
"update {0} p set p.DateUpdated = :dateUpdated, p.DateExpires = :dateExpires where p.id = :id", typeof(T)); <-- passing the type of the entity.
session.CreateQuery(hqlUpdate)
.SetDateTime("dateUpdated",DateUpdated)
.SetDateTime("dateExpires", DateExpires)
.SetParameter("id", Id)
.ExecuteUpdate();
transaction.Commit();
return session.Get<T>(Id);
}
}
}