Updating Entity in EF6 gives primary key exception - c#

When I try to update this object in EF6 I get an error stating more than 1 entity has this primary key. Looking at this DB I know this to be untrue(from what I can see).
I need to be able to update a second object based on one of the properties on the posted object. The code below produces the error. I have left in commented out pieces that I have tried to get this to work.
public async Task<ActionResult> Edit(PricingRule pricingRule)
{
if(ModelState.IsValid)
{
var currentUser = await serv.UserManager.FindByIdAsync(User.Identity.GetUserId());
var company = currentUser.Company;
//var entityRule = serv.PricingService.PricingRules.Get(pricingRule.PricingRuleId);
//If this is the first rule, set it to the company default
var rulesCount = company.PricingRules.Count;
if (rulesCount <= 1 || company.DefaultPricingRule == null)
pricingRule.DefaultPricingRule = true;
//Make sure no other rules are marked as default, and update the company with this rule as default
if (pricingRule.DefaultPricingRule)
{
if (company.DefaultPricingRule != null)
{
var oldRule = serv.PricingService.PricingRules.Get(company.DefaultPricingRule.PricingRuleId);
oldRule.DefaultPricingRule = false;
//serv.PricingService.PricingRules.Update(oldRule);
}
company.DefaultPricingRule = pricingRule;
serv.CoreService.Companies.Update(company);
}
serv.PricingService.PricingRules.Update(pricingRule);
await serv.SaveAllChangesAsync();
return RedirectToAction("Index");
}
return View(pricingRule);
}

Whether or not it is the best practice or how it should technically be done, this is how I solved my problem.
The edited object I was passing in, needed to be marked as modified first, before doing any other operations. I am assuming this is because the context could then grab it and all other operations regarding it would be done "within context". Other wise I think it was trying to add a new object if I tried to attach it to company.DefaultPricingRule.
public async Task<ActionResult> Edit(PricingRule pricingRule)
{
if(ModelState.IsValid)
{
serv.PricingService.PricingRules.Update(pricingRule);
var currentUser = await serv.UserManager.FindByIdAsync(User.Identity.GetUserId());
var company = currentUser.Company;
//If this is the first rule, set it to the company default
var rulesCount = company.PricingRules.Count;
if (rulesCount <= 1 || company.DefaultPricingRule == null)
pricingRule.DefaultPricingRule = true;
//Make sure no other rules are marked as default, and update the company with this rule as default
if (pricingRule.DefaultPricingRule)
{
if (company.DefaultPricingRule != null)
{
var oldRule = serv.PricingService.PricingRules.Get(company.DefaultPricingRule.PricingRuleId);
oldRule.DefaultPricingRule = false;
serv.PricingService.PricingRules.Update(oldRule);
}
company.DefaultPricingRule = pricingRule;
serv.CoreService.Companies.Update(company);
}
await serv.SaveAllChangesAsync();
return RedirectToAction("Index");
}
return View(pricingRule);
}
If Anyone has a comment on if this is best practice or if there is a better way to do it, I gladly take criticism.

Related

Can't insert data in mongodb with insertOneAsync

I want to insert data into a mongodb database. It worked like a charm, but I have one collection where I can't insert in the database and I don't know why. I do literally the same.
This doesnt work here
public async Task AddVerifyAsync(VerifyModel verifyModel)
{
verifyModel.CreationDate = DateTime.Now;
await _context.Verifies().InsertOneAsync(verifyModel);
}
I checked it multiple times, ran the debug mode, checked the model etc, it is all absolutely correct.
This is the method which returns the collection:
public IMongoCollection<VerifyModel> Verifies()
{
return _database.GetCollection<VerifyModel>("VerifyModel");
}
These are the methods that are working:
public async Task AddUserAsync(UserModel user)
{
await _context.Users().InsertOneAsync(user);
}
The collection-returner is just the same as above but with the other types.
I even have tried to remove the DateTime thing. Also I set the _id before giving it to the AddVerifyAsync method. But I even have tried disabling that.
the _context is dependency injected in the constructor.
This is where both methods get used:
public async Task CreateUserAsync(UserModel user)
{
UserModel gotUser = await _userRepository.GetUserByEmailAsync(user.Email);
//check if exists
if (gotUser != null)
throw new DataException();
//set defaults and prevent, that the user tries to manipulate data
user.Active = false;
user.RegTimeStamp = DateTime.Now;
user.AdminLevel = 0;
user.OrganizationId = null;
user.OrganizationAdminLevel = null;
user.Password = BCrypt.Net.BCrypt.HashPassword(user.Password);
user.Balance = 0f;
user.ProfilePicture = "";
//Add the user to the repo
await _userRepository.AddUserAsync(user);
VerifyModel verifyModel = new VerifyModel();
verifyModel.UserId = user.Id;
verifyModel.Id = ObjectId.GenerateNewId().ToString();
//useractivation
verifyModel.Type = 0;
//insert the verifyuser in database
await _verifyRepository.AddVerifyAsync(verifyModel);
await _emailSenderService.SendMailAsync(user, EmailTemplate.RegisterMail, verifyModel.Id,
"Herzlich Willkommen bei flundr. Nur noch ein Schritt!", user.Email);
}
Here the Verifymodel doesnt get inserted in the db.

C# - how to delete row in realm - android xamarin

i tried this method that I created but it prompts me an error:
Realms.RealmInvalidObjectException:This object is detached. Was it deleted from the realm?'
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
using (var transaction = Realm.GetInstance(config).BeginWrite())
{
Realm.GetInstance(config).Remove(denom_list[0]);
transaction.Commit();
}
}
what is the proper coding for deleting records from database in realm in C# type of coding?
You are doing it the right way. The error message you are getting indicates that the object was removed already. Are you sure it still exists in the realm?
UPDATE:
I decided to update this answer because my comment on the other answer was a bit hard to read.
Your original code should work fine. However, if you want deleteFromDatabase to accept lists with CashDenomination instances that either have been removed already or perhaps were never added to the realm, you would need to add a check. Furthermore, note that you should hold on to your Realm instance and use it in the transaction you created. In most cases, you want to keep it around even longer, though there is little overhead to obtaining it via GetInstance.
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
if (!denom_list[0].IsValid) // If this object is not in the realm, do nothing.
return;
var realm = Realm.GetInstance(config);
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom_list[0]);
transaction.Commit();
}
}
Now, if you want to use identifiers, you could look it up like you do, but still just use Remove:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.All<CashDenomination>().FirstOrDefault(c => c.denom_id == denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
Finally, if your CashDenomination has denom_id marked as PrimaryKey, you could look it up like this:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.ObjectForPrimaryKey<CashDenomination>(denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
public void deleteFromDatabase(Realm realm, long cashDenominatorId)
{
realm.Write(() =>
{
var cashDenominator = realm.All<Person>().Where(c => c.Id == cashDenominatorId);
Realm.RemoveRange<CashDenomination>(((RealmResults<CashDenomination>)cashDenominator));
});
}
Which you would call as
Realm realm = Realm.GetInstance(config);
var denom_list = ...
// ...
deleteFromDatabase(realm, denom_list[0].id);
I already made it having this code :) thanks to #EpicPandaForce 's answer.
public void deleteFromDatabase(int denom_ID, int form_ID)
{
//Realm realm;
//and
//RealmConfiguration config = new RealmConfiguration(dbPath, true);
//was initialized at the top of my class
realm = Realm.GetInstance(config);
realm.Write(() =>
{
var cashflow_denom = realm.All<CashDenomination>().Where(c => c.denom_id == denom_ID);
var cashflow_form = realm.All<CashForm>().Where(c => c.form_id == form_ID);
realm.RemoveRange(((RealmResults<CashDenomination>)cashflow_denom));
realm.RemoveRange(((RealmResults<CashForm>)cashflow_form));
});
}
it is now deleting my data without exception :)

Add or Update a Record?

I have an MVC application with the following code in the POST method of the controller. I am doing an EF Add and obviously that is not right. I want it to add the record if it doesn't exist, otherwise Update. How can I do that please?
try
{
AttributeEntities db = new AttributeEntities();
IEnumerable<string> items = viewModel.SelectedAttributes2;
int i = 0;
foreach (var item in items)
{
var temp = item;
// Save it
SelectedHarmonyAttribute attribute = new SelectedHarmonyAttribute();
attribute.CustomLabel = viewModel.ItemCaptionText;
attribute.IsVisible = viewModel.Isselected;
string harmonyAttributeID = item.Substring(1, 1);
// attribute.OrderNumber = Convert.ToInt32(order);
attribute.OrderNumber = i++;
attribute.HarmonyAttribute_ID = Convert.ToInt32(harmonyAttributeID);
db.SelectedHarmonyAttributes.Add(attribute);
db.SaveChanges();
}
}
You would need to check the database for the record you are trying to add/update. If the look-up returns null, that means that it doesn't exist in the database. If it does, you can modify the record that you looked up and call db.SaveChanges() to persist the changes you made to the database.
Edit:
int id = Convert.ToInt32(harmonyAttributeID);
var existingEntry = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == id);
One common way to determine an add or update is by simply looking at an identifier field, and setting the appropriate state.
using System.Data;
SelectedHarmonyAttribute attribute;
using (var db = new YourDbContext())
{
db.Entry(attribute).State = attribute.HarmonyAttribute_ID == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
You could import the System.Data.Entity.Migrations namespace and use the AddOrUpdate extension method:
db.SelectedHarmonyAttributes.AddOrUpdate(attribute);
db.SaveChanges();
EDIT:
I'm assuming that SelectedHarmonyAttributes is of type DbSet
EDIT2:
Only drawback with doing it this way (and it may not be a concern for you), is that your entity isn't responsible for it's own state change. This means that you can update any property of the entity to something invalid, where you might want to internally validate it on the entity itself or maybe do some other processing you always want to occur on update. If these things are a concern for you, you should add a public Update method onto the entity and check for its existence on the database first. e.g:
var attribute = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == harmonyAttributeID);
if (attribute != null)
{
attribute.Update(viewModel.ItemCaptionText, viewModel.Isselected, i++);
}
else
{
attribute = new Attribute(viewModel.ItemCaptionText, viewModel.Isselected);
db.SelectedHarmonyAttributes.Add(attribute);
}
db.SaveChanges();
Your update method might look something like:
public void Update(string customLabel, bool isVisible, int orderNumber)
{
if (!MyValidationMethod())
{
throw new MyCustomException();
}
CustomLabel = customLabel;
IsVisible = isVisible;
OrderNumber = orderNumber;
PerformMyAdditionalProcessingThatIAlwaysWantToHappen();
}
Then make all of the entities' properties public "get" but protected "set" so they can't be updated from outside the entity itself. This might be going off an a bit of a tangent but using the AddOrUpdate method would assume you don't want to control the way an update occurs and protect your domain entity from getting into an invalid state etc. Hope this helps!

Checking individual fields for changes in an EntityState

In our database we have a user. A user can have certain metadata (Like their location, age, etc) associated with them. In addition to that they have a profile image.
A user can edit any of these things at any time.
The problem we run into is that when a user goes to edit their information, but they don't choose an image -- the previous image gets erased (Null).
I figure that when we go to save the modified user, we should be able to say if(user.profileImage == null) don't save it
I figure that would come into play in our user repository which uses the following code:
public void SaveUser(Domain.Entities.User user)
{
if (user.UserId == 0)
{
context.Users.Add(user);
}
else
{
context.Entry(user).State = EntityState.Modified;
//The logic would be here
}
context.SaveChanges();
}
However I seems that no matter what we try it doesn't work.
So my question is: Is there a way to check changes to individual fields instead of the entire EntityState?
I ended up storing image data in the Session and then if the image upload was null I simply inserted the session attribute into my image, effectively keeping the old image if left empty.
Session["image"] = productService.GetProduct(id).Image;
Then in the post I said...
if (image != null)
{
product.ImageType = image.ContentType;
product.Image = new byte[image.ContentLength];
image.InputStream.Read(product.Image, 0, image.ContentLength);
}
else
{
product.Image = (byte[])Session["image"];
}
Yes, there is a way:
context.Users.Attach(user);
context.Entry(user).Property(u => u.Location).IsModified = true;
context.Entry(user).Property(u => u.Age).IsModified = true;
// etc.
Be aware that you cannot set IsModified to false once the property is marked as modified. So, something like this ...
context.Entry(user).State = EntityState.Modified;
context.Entry(user).Property(u => u.Image).IsModified = false;
... does not work.
Edit Alternative solution:
public void SaveUser(Domain.Entities.User user)
{
if (user.UserId == 0)
{
context.Users.Add(user);
}
else
{
var userInDb = context.Users.Single(u => u.UserId == user.UserId);
if (user.profileImage == null)
user.profileImage = userInDb.profileImage;
context.Entry(userInDb).CurrentValues.SetValues(user);
}
context.SaveChanges();
}
This keeps the value of profileImage as it is in the database if the user didn't post a new image (= user.profileImage == null), otherwise it saves the new image.

Entity Framework - Detached Update issue

Ok, I am attempting to use this custom extension to perform an entity update. Right now it should update all properties and then all of its related ends (references). The problem is even though the entity with changes does have a related end with an entity key that is valid and correct (verified in the debugger its there). When the RelationshipManager gets it they key is null.. Anyone see anything wrong with this code?
public static void ApplyChanges(this ObjectContext context, EntityObject entityWithChanges)
{
if (entityWithChanges.EntityState == EntityState.Detached)
{
object entityCurrentlyInDB = null;
if (context.TryGetObjectByKey(entityWithChanges.EntityKey, out entityCurrentlyInDB))
{
context.ApplyPropertyChanges(entityWithChanges.EntityKey.EntitySetName, entityWithChanges);
foreach (var relatedEnd in ((IEntityWithRelationships)entityCurrentlyInDB).RelationshipManager.GetAllRelatedEnds())
{
var oldRef = relatedEnd as EntityReference;
if (oldRef != null)
{
var newRef =
((IEntityWithRelationships)entityWithChanges).RelationshipManager
.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName) as EntityReference;
oldRef.EntityKey = newRef.EntityKey;
}
}
}
else
{
throw new ObjectNotFoundException();
}
}
}
This is just a modified version of what I found
Here
Appreciate the help.
UPDATE:
This is the Update Method
public static void UpdateTemplate(Template template)
{
using (TheEntities context = new TheEntities())
{
context.ApplyChanges(template);
try
{
context.SaveChanges();
}
catch (OptimisticConcurrencyException)
{
context.Refresh(RefreshMode.ClientWins, template);
context.SaveChanges();
}
context.RemoveTracking(template);
}
}
This is the unit test:
[TestMethod]
public void CanUpdateATemplate()
{
Template template = new Template();
template.Name = "Test";
template.Description = "Test";
TemplateType type = TemplateManager.FindTemplateTypeByName("Test");
if (type == null)
{
type = new TemplateType();
type.Name = "Test";
}
template.TemplateType = type;
TemplateManager.AddTemplate(template);
template = TemplateManager.FindTemplateByID(template.TemplateID);
Assert.IsNotNull(template);
type = TemplateManager.FindTemplateTypeByName("Round");
if (type == null)
{
type = new TemplateType();
type.Name = "Round";
}
template.TemplateType = type;
TemplateManager.UpdateTemplate(template);
template = TemplateManager.FindTemplateByID(template.TemplateID);
Assert.IsNotNull(template);
Assert.IsTrue(template.TemplateType.Name == "Round");
TemplateManager.DeleteTemplate(template);
template = TemplateManager.FindTemplateByID(template.TemplateID);
Assert.IsNull(template);
}
This is simply not possible due to the way Detach works in EF.
I found that if I added key information myself I could get the generic call to work and even save it.
The problem is that the moment you go to return an entity after calling Detach on it you lose all relationship data. Ive found some write ups on writing graph managers for each entity but I find that a waste seeing as EF should do this stuff (supposedly it will in v2).
EF simply is not ready for N-Tier deployments.

Categories