Code-Design : Can I write the code to update the last modified date of the entity inside the method that persists an entity? - coding-style

Let's say I have an entity:
Employee {
_id,
name,
lastModifiedTimeStamp
}
and a method to persist it . This persist method can be called from anywhere.
boolean persist(Employee e ){
....
<code to save to db>
...
}
Now if I need to write code that updates the lastModifiedTimeStamp. Is it a violation of Single Responsibility Principle if I write it inside the persist method.
boolean persist(Employee e ){
....
<code to save to db>
...
<code to update lastModifiedTimeStamp>
...
}

Related

I want to get the findAndModify return value of MongoTemplate as a modified value

I am currently using mongoTemplate in Spring boot like this:
public MyEntity update(MyDto dto) {
...
MyEntity result = mongoTemplate.findAndModify(
query, update, MyEntity.class);
return result;
}
query puts in the Criteria that finds the MyEntity to be modified, and update puts the contents to change. However, the returned value is the data before the update. How can I get the modified value right away?
When using findAndModify on the mongoTemplate, you have to explicitly configure it if the updated record shall be returned instead of the original one.
This may be done the following way:
FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);
MyEntity result = mongoTemplate.findAndModify(query, update, findAndModifyOptions, MyEntity.class);
return result;
https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/

Spring Boot JPA : identifier of an instance of bingo.model.Group was altered from 1702 to null

I have a Short Question:
Last Save is working(Last Save will be Update).
But First Save is not working.(First Save will be Insert)
I can't insert this way, how is it possible?
#GetMapping(value = "/delete/{id}")
public String delete(#PathVariable BigInteger id, Model model) {
try {
Group group = groupService.findById(id);
group.setId(null);
group.isLog(true);
// This Save will be Insert Data
groupService.save(group);
group = groupService.findById(id);
group.isLog(true);
//This Save will be Update Data
groupService.save(group);
return "redirect:/accountsGroup/";
} catch (Exception ex) {
return "masters/accountsInfo/groups/index";
}
}
You can't just set the ID null.
The entity is in a managed state and will not be new just because you set the ID to null.
The proper way would be to clone the entity state in a new instance.
You could also try to detach the entity (EntityManager.detach) and then set the ID to null. Maybe it will insert a new row. But as I said this is not the way you should do that.
Read more about entity states here: https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/

How does Entity framework perform Transactions inside asp.net mvc

I am using entity framework inside my asp.net mvc web application, but I can not understand how it will handle multiple transaction accessing the same data.
For example I have the following action method that deelte a collection and then loop through a collection and delete the records:-
[HttpPost]
public ActionResult AssignPermisionLevel2(ICollection<SecurityroleTypePermision> list, int id)
{
repository.DeleteSecurityroleTypePermisions(id);
foreach (var c in list)
{
repository.InsertOrUpdateSecurityroleTypePermisions(c,User.Identity.Name);
}
repository.Save();
return RedirectToAction("AssignPermisionLevel", new { id = id });
}
Which will call the following repository method:-
public void DeleteSecurityroleTypePermisions(int securityroleID)
{
var r = tms.SecurityroleTypePermisions.Where(a => a.SecurityRoleID == securityroleID);
foreach (var c in r) {
tms.SecurityroleTypePermisions.Remove(c);
}
}
&
public void InsertOrUpdateSecurityroleTypePermisions(SecurityroleTypePermision role, string username)
{
var auditinfo = IntiateAdminAudit(tms.AuditActions.SingleOrDefault(a => a.Name.ToUpper() == "ASSIGN PERMISION").ID, tms.SecurityTaskTypes.SingleOrDefault(a => a.Name.ToUpper() == "SECURITY ROLE").ID, username, tms.SecurityRoles.SingleOrDefault(a=>a.SecurityRoleID == role.SecurityRoleID).Name, tms.PermisionLevels.SingleOrDefault(a=>a.ID== role.PermisionLevelID).Name + " --> " + tms.TechnologyTypes.SingleOrDefault(a=>a.AssetTypeID == role.AssetTypeID).Name);
tms.SecurityroleTypePermisions.Add(role);
InsertOrUpdateAdminAudit(auditinfo);
}
So let say two users access the same action method at the same time, so will their transactions conflict with each other? , or all the transaction actions (Deletion & Addition) will execute and then the other transaction will start?
UPDATE
Inside my Controller class i will initiate the repository as follow :-
[Authorize]
public class SecurityRoleController : Controller
{
Repository repository = new Repository();
my second question is . You mentioned that EF will mark the entities for deletion or for insetion, then the sql will execute indie the database. but what if one sql statement delete some entities and the other sql statement from the second transaction delete the other entities , could this conflict happen at the database level ? or once the first sql statement from the first transaction start execution, it will prevent other transactions from being executed ? can you advice ?
This entirely depends on how you implement your DbContext. If your context is instantiated within a controller then each transaction will be contained within that context, i.e.
public class SomeController : Controller
{
var repository = new DbContext();
[HttpPost]
public ActionResult AssignPermisionLevel2(ICollection<SecurityroleTypePermision> list, int id)
{
repository.DeleteSecurityroleTypePermisions(id);
foreach (var c in list)
{
repository.InsertOrUpdateSecurityroleTypePermisions(c,User.Identity.Name);
}
repository.Save();
return RedirectToAction("AssignPermisionLevel", new { id = id });
}
}
Each request will create its own instance of the repository and the two will not conflict on an application level. When SaveChanges is called on a DbContext it is done in a single transaction, and as the repository object is created for each request.
Unfortunately Entity Framework does not delete as you expect, and will delete individual elements rather than the entire table. What is actually happening when you are removing the entities in the first step and adding them in the second is as follows:
Load Entities X,Y, and Z
Mark X,Y, and Z for deletion
Insert new rows A, B and C
Run SQL which deletes X, Y and Z, and inserts A, B and C
Now if two requests come in at the same time what could possibly happen is objects X,Y and Z are both loaded in step 1 by both request contexts. They are both marked for deletion and two sets of A, B and C are set to insert. When the first transaction executes it will be fine, however when the second transaction commits it will not be able to find X, Y and Z as they no longer exist.
You may be able to use a lock over the critical section so that the entities are not loaded before they are deleted by another request. The lock would have to be static so something such as:
public class SecurityRoleController : Controller
{
Repository repository = new Repository();
public static object REQUEST_LOCK = new object();
[HttpPost]
public ActionResult AssignPermisionLevel2(ICollection<SecurityroleTypePermision> list, int id)
{
lock(REQUEST_LOCK)
{
repository.DeleteSecurityroleTypePermisions(id);
foreach (var c in list)
{
repository.InsertOrUpdateSecurityroleTypePermisions(c,User.Identity.Name);
}
repository.Save();
}
return RedirectToAction("AssignPermisionLevel", new { id = id });
}
}
Update 2
There are two sides to your problem, the way SQL handles transactions and the way Entity Framework performs deletes. Without going into massive detail on threading you basically have to lock the action so that the same method cannot execute twice at exactly the same time. This will prevent the context from reading potentially stale/already deleted data.
You can read more on SQL/EF race conditions with this question: Preventing race condition of if-exists-update-else-insert in Entity Framework

Entity Framework Code-First: "The ObjectStateManager cannot track multiple objects with the same key."

I'm running into an issue with Entity Framework code-first in MVC3. I'm hitting this exception:
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
This is addressed many times on SO, but I'm having trouble utilizing any of the suggested solutions in my situation.
Here is a code sample:
FestORM.SaleMethod method = new FestORM.SaleMethod
{
Id = 2,
Name = "Test Sale Method"
};
FestContext context = new FestContext();
//everything works without this line:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
context.Entry(method).State = System.Data.EntityState.Modified;
context.SaveChanges();
EDITED to clarify: I am attempting to update an object that already exists in the database.
Everything works fine without the query noted in the code. In my application, my controller is instantiating the context, and that same context is passed to several repositories that are used by the controller--so I am not able to simply use a different context for the initial query operation. I've tried to remove the entity from being tracked in the ObjectStateManager, but I can't seem to get anywhere with that either. I'm trying to figure out a solution that will work for both conditions: sometimes I will be updating an object that is tracked by the ObjectStateManager, and sometimes it will happen to have not been tracked yet.
FWIW, my real repository functions look like this, just like the code above:
public void Update(T entity)
{
//works ONLY when entity is not tracked by ObjectStateManager
_context.Entry(entity).State = System.Data.EntityState.Modified;
}
public void SaveChanges()
{
_context.SaveChanges();
}
Any ideas? I've been fighting this for too long...
The problem is that this query
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
brings one instance of the SaleMethod entity into the context and then this code
context.Entry(method).State = System.Data.EntityState.Modified;
attaches a different instance to the context. Both instances have the same primary key, so EF thinks that you are trying to attach two different entities with the same key to the context. It doesn't know that they are both supposed to be the same entity.
If for some reason you just need to query for the name, but don't want to actually bring the full entity into the context, then you can do this:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name;
If what you are tying to do is update an existing entity and you have values for all mapped properties of that entity, then the simplest thing to do is to not run the query and just use:
context.Entry(method).State = System.Data.EntityState.Modified;
If you don't want to update all properties, possibly because you don't have values for all properties, then querying for the entity and setting properties on it before calling SaveChanges is an acceptable approach. There are several ways to do this depending on your exact requirements. One way is to use the Property method, something like so:
var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName;
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue;
context.SaveChanges();
These blog posts contain some additional information that might be helpful:
http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx
The obvious answer would be that your not actually saving the method object to the database before you call:
//everything works without this line:
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name;
However, I think perhaps this is just a bit a code you left out.
What if you make your entities inherit from an abstract class ie.
public abstract class BaseClass
{
public int Id { get; set; }
}
Then update your Repository to
public class Repository<T> where T : BaseClass
{
.....
public void Update(T entity)
{
_context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified;
}
}
Also you might want to not set the ID of your SaleMethod and let it be generated by the database. Problem could also be because SaleMethod Object in the database has Id of 2 and then you try to add another SaleMethod object with Id 2.
The error you see stems from trying to add another SaleMethod object with ID of 2 to the ObjectStateManager.

Linq InsertOnSubmit is throwing exception: Object reference not set to an instance of an object

The following throws exception.
The course object that is being passed to InsertOnSubmit is of type Course that is generated by Linq.
public ActionResult Course(CourseViewModel c)
{
Course course = (Course) c; //CourseViewModel is derrived from Course
SchedulerDataContext db = new SchedulerDataContext();
db.Courses.InsertOnSubmit(course); // <- this is where exception is thrown
db.SubmitChanges();
}
There are already questions about this here and here, however, I don't understand their answer. Supposedly I'm not creating an object in time. Which object and what exactly needs to happen?
You need to create the Course object before you try to insert it.
Course course = new Course { ... set the properties .. };
SchedulerDataContext db = new SchedulerDataContext();
db.Courses.InsertOnSubmit(course);
db.SubmitChanges();

Resources