I am updating a field in LOGIN object by using #Query in UserRepository, then imminently do a find to select the login object, the data is not refreshed. I do see the updated record in the database. If I bounce my tomcat server, go to the page on my browser, i will see the refreshed data.
As shown below, I updated LOGIN.enabled to 1 (updateUserLoginEnableByUuid()) then call findByLoginLoginEmail(), the LOGIN.enable is still showing as 0.
It seems like the old record is still cached somewhere, what am I missing?
in UserConroller.java:
#RequestMapping(value = "/user/enableUser/{userUuid}", method = RequestMethod.GET)
public String enableUser(Model aaModel, #PathVariable("userUuid") String userUuid, Principal aaPrincipal, SessionStatus aaStatus) {
this.caService.updateUserLoginEnableByUuid(userUuid, (byte) 1);
aaStatus.setComplete();
return "redirect:/login";
}
in userRepository.java,
#Modifying // Indicates a method should be regarded as modifying query.
#Query("update Login l SET l.enabled = :enabled WHERE l.user.uuid = :uuid")
int updateUserLoginEnableByUuid(#Param("uuid") String uuid, #Param("enabled") byte enabled);
User findByLoginLoginEmail(String loginEmail);
when you're doing DML or SQL queries, which completely bypass the entity manager cache (as in your example). In this case, the state held by the cache doesn't reflect what is in the database because of the queries
To achieve update you need to clear entitymanager cache after you execute the update query to reflect those changes in database.
You can access entitymanager as #PersistenceContext private EntityManager em; and then execute em.clear(); after update query.
Related
To make updates to a record of SQL Server using Entity Framework Core, I query the record I need to update, make changes to the object and then call .SaveChanges(). This works nice and clean.
For example:
var emp = _context.Employee.FirstOrDefault(item => item.IdEmployee == Data.IdEmployee);
emp.IdPosition = Data.IdPosition;
await _context.SaveChangesAsync();
But is there a standard method if I want to update multiple records?
My first approach was using a list passing it to the controller, but then I would need to go through that list and save changes every time, never really finished this option as I regarded it as not optimal.
For now what I do is instead of passing a list to the controller, I pass each object to the controller using a for. (kind of the same...)
for(int i = 0; i < ObjectList.Count; i ++)
{
/* Some code */
var httpResponseObject = await MyRepositories.Post<Object>(url+"/Controller", Object);
}
And then do the same thing on the controller as before, when updating only one record, for each of the records...
I don't feel this is the best possible approach, but I haven't found another way, yet.
What would be the optimal way of doing this?
Your question has nothing to do with Blazor... However, I'm not sure I understand what is the issue. When you call the SaveChangesAsync method, all changes in your context are committed to the database. You don't have to pass one object at a time...You can pass a list of objects
Hope this helps...
Updating records in bulk using Entity Framework or other Object Relational Mapping (ORM) libraries is a common challenge because they will run an UPDATE command for every record. You could try using Entity Framework Plus, which is an extension to do bulk updates.
If updating multiple records with a single call is critical for you, I would recommend just writing a stored procedure and call if from your service. Entity Framework can also run direct queries and stored procedures.
It looks like the user makes some changes and then a save action needs to persist multiple records at the same time. You could trigger multiple AJAX calls—or, if you need, just one.
What I would do is create an endpoint—with an API controller and an action—that's specific to your needs. For example, to update the position of records in a table:
Controller:
/DataOrder
Action:
[HttpPut]
public async void Update([FromBody] DataChanges changes)
{
foreach(var change in changes)
{
var dbRecord = _context.Employees.Find(change.RecordId);
dbRecord.IdPosition = change.Position;
}
_context.SaveChanges();
}
public class DataChanges
{
public List<DataChange> Items {get;set;}
public DataChangesWrapper()
{
Items = new List<DataChange>();
}
}
public class DataChange
{
public int RecordId {get;set;}
public int Position {get;set;}
}
The foreach statement will execute an UPDATE for every record. If you want a single database call, however, you can write a SQL query or have a stored procedure in the database and pass the data as a DataTable parameter instead.
I've read multiple questions similar to this one but none are exactly my situation.
Using linq-to-sql I insert a new record and submit changes. Then, in the same web request, I pull that same record, and update it, then submit changes. The changes are not saved. The DatabaseContext is the same across both these operations.
Insert:
var transaction = _factory.CreateTransaction(siteId, userId, questionId, type, amount, transactionId, processor);
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.Amount = amount;
_transactionRepository.Add(transaction);
unitOfWork.Commit();
}
Select and Update:
ITransaction transaction = _transactionRepository.FindById(transactionId);
if (transaction == null) throw new Exception(Constants.ErrorCannotFindTransactionWithId.FormatWith(transactionId));
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.CrmId = crmId;
transaction.UpdatedAt = SystemTime.Now();
unitOfWork.Commit();
}
Here's the unit of work code:
public virtual void Commit()
{
if (_isDisposed)
{
throw new ObjectDisposedException(GetType().Name);
}
_database.SubmitChanges();
}
I even went into the designer.cs file and put a breakpoint on the field that is being set but not updated. I stepped through and it entered and execute the set code, so the Entity should be getting "notified" of the change to this field:
public string CrmId
{
get
{
return this._CrmId;
}
set
{
if ((this._CrmId != value))
{
this.OnCrmIdChanging(value);
this.SendPropertyChanging();
this._CrmId = value;
this.SendPropertyChanged("CrmId");
this.OnCrmIdChanged();
}
}
}
Other useful information:
ObjectTracking is enabled
No errors or exceptions when second SubmitChanges is called (just silently fails update)
SQL profiler shows insert and select but not the subsequent update statement. Linq-To-Sql is not generating the update statement.
There is only one database, one database string, so the update is not going to another database
The table has a primary key.
I don't know what would cause Linq-To-Sql to not issue the update command and not raise some kind of error. Perhaps the problem stems from using the same DataContext instance? I've even refreshed the object from the database using the DataContact.Refresh method before it is pulled for the update, but that didn't help.
I have found what is likely to be the root cause. I am using Unity. The initial insert is being performed in a service class with a PerWebRequest lifetime. The select and update is happening in a class with a Singleton lifetime. So my assumption that the DataContext instances are the same was incorrect.
So, in my class with the Singleton lifetime, I get a fresh instance of the database repository and perform the update and no problem.
Now I still don't know why the original code didn't work and my approach could still be considered more a workaround than a solution, but it did solve my problem and hopefully will be useful to others.
I am using this Entity class with Entity Framework 5 Code First:
public class Survey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string SurveyName { get; set; }
[Required]
public int ClientID { get; set; }
[ForeignKey("ClientID")]
public virtual Client Client { get; set; }
}
And in my Controller's Create method I do this:
Survey entity = new Survey()
{
SurveyName = "Test Name",
ClientID = 4
};
db.Surveys.Add(entity);
db.SaveChanges();
Client c1 = entity.Client; //Why is this null?
Client c2 = db.Clients.Find(entity.ClientID); //But this isn't?
string s2 = c2.ClientName;
string s1 = c1.ClientName; //null reference thrown here
The Client navigation property remains null after SaveChanges. I expected the call to load the Client from the database because the foreign key exists. Why didn't it do that?
EDIT
The code here comes from when my controllers were dependent on DbContext. Not long after I got this working I re-factored the code to use repositories and a unit of work. Part of that move was driven by the fact that it just felt wrong to use Create when I wanted to use new. What happened then was that I hit a problem with how to ensure proxies are created when using the repository pattern.
To ensure that lazy loading of a navigation property will work after you've created the parent you must not create the Survey with the new operator but create it by means of the context instance because it will instantiate a dynamic proxy that is capable to lazily load the related Client. That's what the DbSet<T>.Create() method is for:
Survey entity = db.Surveys.Create();
entity.SurveyName = "Test Name";
entity.ClientID = 4;
db.Surveys.Add(entity);
db.SaveChanges();
Client c1 = entity.Client;
string s1 = c1.ClientName;
// will work now if a Client with ID 4 exists in the DB
Just to emphasize: It's not the line entity.ClientID = 4; or db.Surveys.Add(entity); or db.SaveChanges that loads the client from the DB, but the line Client c1 = entity.Client; (lazy loading).
Like #NicholasButler said, calling SaveChanges does what it says on the tin - you can see this if you debug your code: the Intellitrace output will show the SQL it has generated for the insert/update you are persisting, but there will be no subsequent select.
Keep in mind that unless you are eager loading (using the Include method), related entities are not loaded when performing a retrieval, so it stands to reason that creating/updating them wouldn't either.
The Entity Framework (from I think versions 4.1 and up) supports lazy loading. What this means is that if it's enabled, code like Client c1 = entity.Client; should load up that Client object. To be clear, this operation is not directly related to the SaveChanges call.
It would pay to check whether db.Configuration.LazyLoadingEnabled is set to true. If not, try setting it to be true and see if Client c1 = entity.Client; is still null.
In short, calling SaveChanges does not trigger a load, but if lazy loading is enabled, accessing entity.Client should trigger a load of the entity if it hasn't already been loaded.
Edit:
I should've though of this earlier, but you aren't going to be getting lazy loading on your Survey entity object. The reason is that EF works its lazy loading magic by creating a class derived from your one but overriding the properties marked as virtual to support lazy loading. It does this when you perform a retrieval, so your entity object will not lazy load anything as it stands.
Try this just after your call to SaveChanges:
Survey entity2 = db.Surveys.Find(entity.ID);
Client c1 = entity2.Client;
This should exhibit the behaviour you are after.
You need to define all the properties on the Survey class as virtual to enable lazy-loading.
See http://msdn.microsoft.com/en-us/library/vstudio/dd468057(v=vs.100).aspx for more information.
I expected the call to load the Client from the database because the foreign key exists. Why didn't it do that?
It didn't do that because you haven't asked it to. After the call to SaveChanges(), EF doesn't have the data in the referenced row and it won't make a potentially redundant database call to get it.
Calling db.Clients.Find(... tells EF to go and fetch the row from the database, which is why it returns the object.
#In
Identity identity;
Boolean newValue = identity.hasPermission(target, action);
Any call to the above method also does a "select role from Role r" call, which is called from the underlying seam engine. How do I set the query cache for this call as a query hint (e.g. org.hibernate.cacheable flag) so that it doesn't get called again.
Note: Role information is never bound to change, hence I view this as a unnecessary sql call.
I am not in hibernate, but as this question is still unanswered: we extended the standard Identity class of seam for several reasons. You might want to extend it as well to help you caching the results.
As this cache is session scoped, it will have the possible benefit that it will be reloaded when the user logs on/off again - but this depends on your requirements.
Best regards,
Alexander.
/**
* Extended Identity to implement i.e. caching
*/
#Name("org.jboss.seam.security.identity")
#Scope(SESSION)
#Install(precedence = Install.APPLICATION)
#BypassInterceptors
#Startup
public class MyIdentity extends Identity {
// place a concurrent hash map here
#Override
public boolean hasPermission(Object name, String action) {
// either use the use the cached result in the hash map ...
// ... or call super.hasPermission() and cache the result
}
}
Much of my application uses complied queries to retrieve data. In these queries I'll often refer to the current user. I'm noticing that if a user, B, logs in after another user, A, then user B will see user A's information.
I have queries much like this all through out the application
public static Func<DataContext, MyRecord> CurrentUserRecords =
CompiledQuery.Compile<DataContext, MyRecord>(
(DataContext db) =>
(from r in db.MyRecords
where
r.User == User.Current
select r).SingleOrDefault());
User.Current is a static property that changes depending on who's logged in.
public static User Current
{
get { return MyBase<User>.Get((int)(HttpContext.Current.Session["CurrentUserID"] ?? 0)); }
}
When I login for the first time with User A, the above compiled query returns User A's records. It follows that User.Current also returns the proper reference to User A. However, when I log in as User B, the above compiled query still returns User A's records, despite the fact that User.Current is returning a reference to User B.
I ran Profiler for SQL Server, and noticed when the compiled query was executed the generated TSQL referenced User A's ID both times.
So my question is this:
Do compiled queries somehow cache?
If so, what is there life span, and can I control it?
Is referencing a "current user" in a compiled query bad design for an ASP.net application?
Thanks all!
You need to allow a string parameter in the compiled query. Otherwise it will resolve the string's value during .Compile(). Try this:
public static Func<DataContext, string, MyRecord> UserRecordByParam =
CompiledQuery.Compile<DataContext, string, MyRecord>
(
(DataContext db, string UserName) =>
db.MyRecords.Where( r => r.User == UserName ).SingleOrDefault()
);
public static Func<DataContext, MyRecord> CurrentUserRecord =
(DataContext db) => UserRecordByParam(db, User.Current);