Not to sound like a broken record here (there a few posts that look like this one) but none of them seem to solve my problem. It seems that when you want to update
private bool resetPassword(string password)
{
try
{
var db = new SchedulerDBDataContext();
// since this is a instance method, I grab the ID from _this_
AdminUser user = db.AdminUsers.SingleOrDefault(t => t.ID == _ID);
if (user != null)
{
// this method DOES update these two fields.
SchedUtil.md5Hash(password, ref user._EncryptedPassword, ref user._PasswordSalt);
// I threw these in there to try something... it didn't work.
//user._EncryptedPassword = user.EncryptedPassword;
//user._PasswordSalt = user.PasswordSalt;
// this DOESN'T do anything.
db.SubmitChanges();
return true;
}
return false;
}
catch (Exception)
{
return false;
}
}
Maybe this a dumb question but I'm retrieving this from the db... why not just update this's properties. I'm guess I need to pull it through the DBContext I guess.
You should be setting the public properties and not the private values.
// I threw these in there to try something... it didn't work.
//user._EncryptedPassword = user.EncryptedPassword;
//user._PasswordSalt = user.PasswordSalt;
This won't trigger any updates.
Even if you do :
user.EncryptedPassword = user._EncryptedPassword;
user.PasswordSalt = user._PasswordSalt;
this won't trigger any change either as you are not actually changing the values
You can do something like
string newEncryptedPassword;
string newPasswordSalt;
SchedUtil.md5Hash(password, ref newEncryptedPassword, ref newPasswordSalt);
user.EncryptedPassword = newEncryptedPassword;
user.PasswordSalt = newPasswordSalt;
Also check that your table has a primary key, otherwise Linq will not track the changes.
DJ,
Are you sure
user._EncryptedPassword ,
user._PasswordSalt
are the properties ? I think you LINQ TO SQL creates public and private properties.
Can you set them
user.EncryptedPassword ,
user.PasswordSalt
like this ?
Ved
To troubleshoot your code, try any of these suggestions:
while debugging the code, I'll assume that your object is not null.
ensure that your properties are actually changed. It's odd that you're using pipe-prefixed field names, but either way: while debugging, check that your properties actually have new values.
use SQL Server Profiler to capture the SQL statement sent to the database server. You'll then be able to re-run this UPDATE query back into SQL Management Studio, and determine how many records are effected. You'll also be able to see the values passed in the UPDATE statement.
Ved pointed out one possible problem. Just in case that doesn't work, you should double check your LINQ to SQL class' AdminUser class definition and make sure that the generated code implements the INotifyPropertyChanging and INotifyPropertyChanged interfaces. There are some cases where the designer will not implement these interfaces which prevents updates from working. e.g., not declaring a primary key.
Related
I am having an infuriating issue where one paticular column in my entity will not save/update. I have tried numerous methods of updating the row such as manally assigning each property I want to update, to where I am now (see code block below). 2 columns update and save as expected (absent_type_id, and point_value) however no matter what I do the "description" column just will not save.
I have checked the debugger to see what the value is before the db.SaveChanges(); and confirmed it is set to the new value; however, selecting the row after the save (and checking the database) shows that the others values are updated, but the description reverts back to what it was...why could this be?
[HttpPost]
public JsonResult UpdateOccurrence(int occ_id,
string absent_type,
string description,
int point_value)
{
try
{
// Get id for the absent type
int absent_type_id = db.AT_absent_types.Single(a => a.absent_type == absent_type).absent_type_id;
var occurrenceToUpdate = new AT_occurrences
{
occ_id = occ_id,
absent_type_id = absent_type_id,
description = description,
point_value = point_value
};
db.AT_occurrences.Attach(occurrenceToUpdate);
db.SaveChanges();
//return call omitted
}
catch (Exception ex)
{
return Json(new {Result = "ERROR", Message = ex.Message});
}
}
As I mentioned - posting the point_value and absent_type save and update the entity perfectly, the description just will not save! i have no idea why. Any help or insight would be very much appreciated - this is driving me crazy! Cheers!
This may not be the answer you're looking for, but whenever I face a similar issue with EF, I delete the table completely, then build. If you're using TFS, you can always undo your pending changes if this doesn't work, which is the caveat I use when I do this for myself.
You use "Attach" to attach a disconnected property to the context. My understanding is Entity Framework has no way of knowing that the properties you're changing are different from those in the DB. So you have to explicitly tell the EF that you've changed a property.
Like this:
db.Entry(occurenceToUpdate).Property(o=>o.occ_id).IsModified = true;
or like this:
db.Entry(occurenceToUpdate).Property("occ_id").IsModified = true;
You'll have to repeat it for each properties you're setting.
I know for sure the above works but you can also try the following. If you're setting all the properties of the entity object, check if something like this works:
db.Entry(occurenceToUpdate).State = EntityState.Modified.
My code seems straightforward:
bool rv = false;
var results = from user in Users
where user.userName.Equals(newUser.userName)
select user;
if (results.Count() == 0)
{
this.context.Users.Add(newUser);
this.context.SaveChanges();
rv = true;
}
return rv;
But this causes a DbEntityValidationException with an inner exception value that says:
OriginalValues cannot be used for entities in the Added state.
...What does that even mean? The only thing I can think of is that newUser shows a value of 0 for userID even though this has a private setter and userID, as a primary key, is supposed to be database-generated. But if that were the problem, it would not be possible to simply use Add() to insert an object into any EF database ever. So I'm confused.
Thanks in advance to anyone who can help shed some light on this.
ETA: newUser is created by the following code in my Controller:
[HttpPost]
public ActionResult CreateRegistration(FormVMRegistration newRegistration)
{
//draw info from form and add a new user to repository
User newUser = new User();
newUser.userName = newRegistration.userName;
newUser.screenName = newRegistration.screenName;
newUser.password = newRegistration.usersPW;
bool addSuccess = userRep.Add(newUser);
...
}
FormVMRegistration is just a ViewModel with nothing but string properties, to convey data from the Regiostration form to the Controller.
userRep is my User Repository.
I think it comes from the fact that you're using the newUser object before it is saved. Try to change this line
bool addSuccess = userRep.Add(newUser);
to
bool addSuccess = userRep.Add(newUser, newRegistration.userName);
and then (given passedUserName is a passed name from above) change your linq query to:
var results = from user in Users
where user.userName.Equals(passedUserName)
select user;
Good luck!
As near as can be determined, the problem seems to have stemmed from "deep" references to objects, or references to references. IOW, class Foo made a reference to class Bar which made a reference to class Baz which made a reference to class Foo... and apparently EF doesn't like this so much, because it's not easy to validate.
So I'm have a really hard time figuring out when I should be attaching to an object and when I shouldn't be attaching to an object. First thing's first, here is a small diagram of my (very simplified) object model.
In my DAL I create a new DataContext every time I do a data-related operation. Say, for instance, I want to save a new user. In my business layer I create a new user.
var user = new User();
user.FirstName = "Bob";
user.LastName = "Smith";
user.Username = "bob.smith";
user.Password = StringUtilities.EncodePassword("MyPassword123");
user.Organization = someOrganization; // Assume that someOrganization was loaded and it's data context has been garbage collected.
Now I want to go save this user.
var userRepository = new RepositoryFactory.GetRepository<UserRepository>();
userRepository.Save(user);
Neato! Here is my save logic:
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
//DataContext.Organizations.Attach(user.Organization);
DataContext.Users.InsertOnSubmit(user);
}
else
{
DataContext.Users.Attach(user);
}
DataContext.SubmitChanges();
// Finished here as well.
user.Detach();
}
So, here we are. You'll notice that I comment out the bit where the DataContext attachs to the organization. If I attach to the organization I get the following exception:
NotSupportedException: An attempt has been made to Attach or Add an
entity that is not new, perhaps having
been loaded from another DataContext.
This is not supported.
Hmm, that doesn't work. Let me try it without attaching (i.e. comment out that line about attaching to the organization).
DuplicateKeyException: Cannot add an entity with a key that is already
in use.
WHAAAAT? I can only assume this is trying to insert a new organization which is obviously false.
So, what's the deal guys? What should I do? What is the proper approach? It seems like L2S makes this quite a bit harder than it should be...
EDIT: I just noticed that if I try to look at the pending change set (dataContext.GetChangeSet()) I get the same NotSupportedException I described earlier!! What the hell, L2S?!
It may not work exactly like this under the hood, but here's the way I conceptualize it: When you summon an object from a DataContext, one of the things Linq does is track the changes to this object over time so it knows what to save back if you submit changes. If you lose this original data context, the Linq object summoned from it doesn't have the history of what has changed in it from the time it was summoned from the DB.
For example:
DbDataContext db = new DbDataContext();
User u = db.Users.Single( u => u.Id == HARD_CODED_GUID );
u.FirstName = "Foo";
db.SubmitChanges();
In this case since the User object was summoned from the data context, it was tracking all the changes to "u" and knows how to submit those changes to the DB.
In your example, you had a User object that had been persisted somewhere (or passed from elsewhere and do not have it's original DataContext it was summoned from). In this case, you must attach it to the new data context.
User u; // User object passed in from somewhere else
DbDataContext db = new DbDataContext();
u.FirstName = "Foo";
DbDataContext.Users.Attach( u );
db.SubmitChanges();
Since the relationship between user and organization is just a GUID (OrganizationId) in your data model, you only have to attach the user object.
I'm not sure about your scaffolding code, but maybe something like this:
private const Guid DEFAULT_ORG = new Guid("3cbb9255-1083-4fc4-8449-27975cb478a5");
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
// via a GUID, not by assigning an
// Organization object
DataContext.Users.InsertOnSubmit(user);
}
else
{
DataContext.Users.Attach(user);
}
DataContext.SubmitChanges();
}
So "attach" is used when you take an object that exists from the database, detach it (say by marshalling it over a webservice somewhere else) and want to put it back into the database. Instead of calling .Attach(), call .InsertOnSubmit() instead. You're almost there conceptually, you're just using the wrong method to do what you want.
I used an big table with 400+ columns. No way am I going to map and test all that!
Get the original object from database, and attach it with the amended object. Just make sure the object coming back in is fully populated other wise it will override it the DB with blanks!
Or you can copy the original GET into memory and work on a proper copy (not just reference) of the MOdel, then pass the original and the changed one in, instead of re getting like I do in the example. This is just an example of how it works.
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
// via a GUID, not by assigning an
// Organization object
DataContext.Users.InsertOnSubmit(user);
}
else
{
var UserDB = DataContext.Users.FirstOrDefault(db => db.id == user.id); //Costs an extra call but its worth it if oyu have 400 columns!
DataContext.Users.Attach(user, userDB); //Just maps all the changes on the flu
}
DataContext.SubmitChanges();
}
I am writing a CRUD using winforms, connecting to MS SqlServer 2008 via Linq2Sql.
When the user of my application wants to delete a record in the database I dont't want it physically deleted. I just want to set a column called "DlDat" to the current datetime and then update the record instead of deleting it. To force this behaviour in Linq I extend the Delete(table) method autogenerated by sqlmetal.
If e.g. I have a database table called "Unit" and a datacontext called "PdpDataContext" the code looks like this:
public partial class PdpDataContext
{
public PdpDataContext() : base()
{
OnCreated();
}
partial void DeleteUnit(Unit instance)
{
instance.DLDAT = DateTime.Now;
this.ExecuteDynamicUpdate(instance);
}
partial void UpdateUnit(Unit instance)
{
instance.CHDAT = DateTime.Now;
this.ExecuteDynamicUpdate(instance);
}
}
But when I do the delete:
PdpDataContext cx = new PdpDataContext();
cx.ObjectTrackingEnabled = true;
Unit u = new Unit();
u = cx.Unit.Single(x => x.INTUNITNO == 1);
cx.Unit.DeleteOnSubmit(u);
cx.SubmitChanges();
I get an SqlException "wrong syntax near WHERE". Logging the SQL output of the context I see that Linq tries to do an "empty" update:
UPDATE [dbo].[Unit]
SET
WHERE ([INTUNITNO] = #p0) AND ([version] = #p1)
This may be because I use ExecuteDynamicUpdate when the ChangeSet of the context contains only a "delete" and no "update".
I could work around this by just updating the record instead of deleting it when the user presses the delete button or pherhaps by using a stored procedure. But since I am new to the world of Linq I wonder if there is another way by using the datacontext.
The problem with doing it that way is, linq assumes that you are going to use the same CRUD operation that you are overriding. Take a look at this article (Responsibilities of the Developer In Overriding Default Behavior (LINQ to SQL))
This is what I would do:
partial void DeleteUnit(Unit instance)
{
//instance.DLDAT = DateTime.Now;
//this.ExecuteDynamicUpdate(instance);
PdpDataContext cx = new PdpDataContext();
cx.ObjectTrackingEnabled = true;
Unit u = new Unit();
u = cx.Unit.Single(x => x.INTUNITNO == 1);
u.DLDAT = DateTime.Now;
cx.SubmitChanges();
}
I wonder if it wouldn't be better to use an "INSTEAD OF" trigger (delete) on the table... or just don't lie to LINQ: if you want to do an UPDATE (not a DELETE), then update the object (don't delete it). For example, write a method on the data-context that simulates delete, rather than using DeleteOnSubmit.
You may also be able to do something with overriding SubmitChanges and investigating the deltas, but I'm not sure it is a good idea
The following simplified code doesn't work (because it sets the retrieved object reference to the parameter), but it shows what I want to do
public bool updateEvent(clubEvent newEvent)
{
TNightCon tCon = new TNightCon();
clubEvent cEv = (from cEvent in tCon.clubEvents
where cEvent.EventID == newEvent.EventID
select cEvent).First();
// Won't work, but do I have to set all the fields manually?
cEv = newEvent;
tCon.SubmitChanges();
return true;
}
Or, alternately,
tCon.ClubEvents.DeleteOnSubmit(cEv);
tCon.CLubEvents.InsertOnSubmit(newEvent);
tCon.SubmitChanges();
You don't need to retrieve the current object to do what you need to do. If the Ids are the same, all you need to do is attach the new object to your context. The trick then becomes getting Linq2SQL to treat the object as "dirty". Timothy Khouri has a blog post that details a useful technique using the Refresh method on the context. Here's what it'd look like.
public bool updateEvent(clubEvent newEvent)
{
tCon.clubEvents.Attach(newEvent);
tCon.Refresh(RefreshMode.KeepCurrentValues, settings)
tCon.SubmitChanges();
return true;
}