Why I get ID = 0 when new row inserted to the table? - c#

I use Entity frameWork to save new row to my database.
I created method that insert new object to the table.The object is added to the table but method returns me always 0 .While I expect the new Id.
Here is the method:
private int SaveVectorLayer(VVectorLayer layer)
{
if (layer == null) return 0;
Data.VectorLayer vectorLayer;
if (layer.Id == 0)
{
vectorLayer = new Data.VectorLayer();
_context.Entry(vectorLayer).State = EntityState.Added;
}
else
{
vectorLayer = _context.VectorLayers.Find(layer.Id);
if (vectorLayer == null) throw new ObjectNotFoundException(string.Format("Layer with id={0} not found!", layer.Id));
}
vectorLayer.Title = layer.Title;
if (layer.Style != null) layer.Style.SaveStyle(vectorLayer);
vectorLayer.MinScale = layer.MinScale;
vectorLayer.MaxScale = layer.MaxScale;
vectorLayer.GeomType = layer.GeomType ?? "Point";
_context.SaveChanges();
return layer.Id;
}
Any idea why returned Id is always 0?

Your return statement should be vectorLayer.Id.
You are currently returning the Id of the object that was not found in the database and had an Id of 0.
In addition regarding the following code:
vectorLayer.Title = layer.Title;
if (layer.Style != null) layer.Style.SaveStyle(vectorLayer);
vectorLayer.MinScale = layer.MinScale;
vectorLayer.MaxScale = layer.MaxScale;
vectorLayer.GeomType = layer.GeomType ?? "Point";
Consider encapsulating it using a clone like method or a Copy Constructor.

You need to return vectorLayer.Id. You never actually set the layer.Id anywhere. vectorLayer is what you create and add to the DB.

There are several problems with this code.
As everyone else noticed, you are saving vectorLayer, not layer. If you want to return an ID, you should return vectorLayer.Id
Your code returns 0 if the input is missing :
if (layer == null) return 0;
That's not a good idea. It means that you can't differentiate between actual 0s and invalid input. If this is unexpected, throw an exception. If it is, change the method to bool TrySaveVectorLayer(layer,out int id), or (bool ok,int id) SaveVectorLayer(layer)`.
Worst case, return a negative number or some value that's clearly invalid, so you know at least what happened.
It could be something else entirely though, like an ID field that isn't autogenerated, eg through an IDENTITY() or SEQUENCE

return layer.Id; //this id is of wrong object
The above line is returning the ID of the VVectorLayer. But you actually want to return the ID of the inserted data row. That will be the Id in the entity vectorLayer.
So you have to return vectorLayer.Id;

Related

Adding same entity object more than once with EF Core

In below code , I want to add myEntity object more than once to database using EF Core. but each time with different value on property id but all other properties are the same. How can I do this? because it's only adding 1 row in the database.
I want to do this because I don't want to repeat calling GetCurrentLocalDateTime() for each iteration and also in the else statement.
var myEntity = _mapper.Map<AEntity >(entityDto);
myEntity.updatedAt = _helper.GetCurrentLocalDateTime();
myEntity.CreatedAt = _helper.GetCurrentLocalDateTime();
if (entityDto.ids != null || entityDto.ids.Count > 0)
{
foreach (var id in entityDto.ids)
{
myEntity.id = id;
await _dbContext.myEntities.AddAsync(myEntity);
}
}
else
{
await _dbContext.myEntities.AddAsync(myEntity);
}
await _dbContext.SaveChangesAsync();
You can't add a single instance of a class, change one of its properties, and add it again expecting a new instance to be added to your database. All that will happen is that you change the property of the single instance you have added.
Instead, you will need to map the DTO multiple times, so that you add multiple instance of your entity class to the DbSet.
You also need to use && instead of || in your if condition. Use OR (||) will result in a NullReferenceException if the entityDto.ids collection is null.
var now = _helper.GetCurrentLocalDateTime();
if (entityDto.ids != null && entityDto.ids.Count > 0)
{
foreach (var id in entityDto.ids)
{
var myEntity = _mapper.Map<AEntity>(entityDto);
myEntity.updatedAt = now;
myEntity.CreatedAt = now;
myEntity.id = id;
await _dbContext.myEntities.AddAsync(myEntity);
}
}
else
{
var myEntity = _mapper.Map<AEntity>(entityDto);
myEntity.updatedAt = now;
myEntity.CreatedAt = now;
await _dbContext.myEntities.AddAsync(myEntity);
}
await _dbContext.SaveChangesAsync();
It uses it to add multiple data.Look at these
.AddRangeAsync()
.AddRange()
you can try to keep your data in list and save it all at once with "addrange()"
Yes, it is possible, though I would use caution to ensure that this is only to insert a number of copies:
foreach (var id in entityDto.ids)
{
myEntity.id = id;
await _dbContext.myEntities.AddAsync(myEntity);
_dbContext.Entry(myEntity).State = EntityState.Detached;
}
By detaching the entity, the DbContext will no longer be tracking it, so updating it and adding it again will be treated as a request to add a new entity.
Normally this kind of code results in a bug where developers are trying to reuse a single entity instance to update multiple data rows. This is a rather odd requirement to insert several copies of the same data, so I would ensure it is documented well so future developers don't try repurposing it. :)

Checking to see if string exists in db using linq

Here's my attempt:
public void ReadLot(LotInformation lot)
{
try
{
using (var db = new DDataContext())
{
var lotNumDb = db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(r.lot_number));
if (lotNumDb.lot_number != null && lotNumDb.lot_number.Length == 0)
{
Console.WriteLine("does not exist. yay");
var lotInfo = db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(lotNumber));
}
else if (lotNumDb.lot_number.ToString().Equals(lot.lot_number))
{
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
}
Here what I want to do:
When the user uploads a file, I check if the deserialized string from memory is a duplicate in the database or not. If it is, pop up a dialog box saying it's a duplicate/already exists and have nothing happen afterward. If it is not a duplicate, proceed with application. Also, if the column in the table in the database is null, store the lot number there and proceed.
I noticed a few things. If the database is empty and I run the above, I get a null exception because I'm trying to find a lot number in db that is not there. How do I change the code above so that if I check in db and the column is null, then just add the number and not throw an exception when comparing. I think that might be the only problem right now.
I'm not sure what this is supposed to be doing, but you don't need it:
var lotNumDb =
db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(r.lot_number));
Instead, just check for the existance of the lot_number passed to the method, and use Any to determine whether there were any matches. If it returns true, then the lot number is already in the database.
// Check for duplicates
var isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (isDuplicate)
{
// Inform user that the lot_number already exists
return;
}
Console.WriteLine("does not exist. yay");
// Store the lot_number in the database
bool lotNumDbExists = db.LotInformation(r => r.lot_number.Equals(r.lot_number)).Any;
or .exists
This should return either a true or false of if it exists.

Object reference not set to an instance of an object - C# Error

I know this gets asked a lot and I have read through similar posts but I can't figure my error out. I am getting the Object reference not set to an instance of an object error with a .aspx.cs file.
Here is a snippet of my code...the error is thrown by the String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id.ToString()) line.
Note: context is declared further up in my code and the enduserDropdown is a control I created using ASP.
foreach (var accountInCrm in context.AccountSet.ToList())
{
if (accountInCrm.StateCode == 0)
{
if (String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id.ToString()))
{
enduserDropdown.Items.Add(new ListItem(accountInCrm.Name, accountInCrm.Id.ToString()));
}
I know that accountInCrm is not empty and If I just remove the String.IsNullOrEmpty If statement all is well, and values are added to my dropdown.
Any ideas what would be causing this?
EDIT:------------------
foreach (var accountInCrm in context.AccountSet.ToList())
{
if (accountInCrm.StateCode == 0)
{
if (accountInCrm.ParentAccountId != null)
{
enduserDropdown.Items.Add(new ListItem("test 2", accountInCrm.Id.ToString()));
}
else
{
enduserDropdown.Items.Add(new ListItem("test 3", accountInCrm.Id.ToString()));
}
}
}
EDIT 2:-------------retrieving data from CRM
var context = new XrmServiceContext();
foreach (var accountInCrm in context.AccountSet.ToList())
The loop goes through each account record in CRM. I know for sure that not all of the accounts in CRM have blank Parent Account Id's, some do but the majority don't.
Your ParentAccountId is probably null:
if (accountInCrm.ParentAccountId != null &&
String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id.ToString()))
{
...
}
Your ParentAccountID could be null and thus you need to check for it.
if (accountInCrm.ParentAccountId != null && String.IsNullOrEmpty(accountInCrm.ParentAccountId.Id))
{
...
}
Also keep in mind that you should not try to convert ID to a string.
If ID is already a string then there is no point in doing a ToString on it and, if it is null, calling ToString on a null reference will crash again your code.
If ID is not a string then you should probably not call String.IsNullOrEmpty on a non string object.
You're doing your Linq To CRM all wrong by doing all of your filtering on the client side. You also want to only select the values that you actually need:
foreach(var account in AccountSet.Where (a => a.StateCode.Value == 0 && a.ParentAccountId != null).
Select(a => new Account { Name = a.Name, AccountId = a.AccountId})
{
enduserDropdown.Items.Add(new ListItem(accountInCrm.Name, accountInCrm.Id.ToString()));
}
As long as enduserDropdown is not null that shouldn't be an issue.

Element not getting added in List

I have a class called Estimate and it has the following field and property:
private IList<RouteInformation> _routeMatrix;
public virtual IList<RouteInformation> RouteMatrix
{
get
{
if (_routeMatrix != null && _routeMatrix.Count > 0)
{
var routeMatrix = _routeMatrix.ToList();
routeMatrix =
routeMatrix.OrderBy(tm => tm.Level.LevelType).ThenBy(tm => tm.Level.LevelValue).ToList();
return routeMatrix;
}
else return _routeMatrix;
}
set { _routeMatrix = value; }
}
So, in the getter method, I am just sorting the _routeMatrix by Level Type and then by Level Value and returning the sorted list.
In one of my programs, I have the following code:
public void SaveApprovers(string[] approvers)
{
int i = 1;
foreach (var approver in approvers)
{
var role = Repository.Get<Role>(long.Parse(approver));
var level = new Models.Level
{
LevelType = LevelType.Approver,
LevelValue = (LevelValue)i,
Role = role
};
Repository.Save(level);
var routeInformation = new Models.RouteInformation
{
Level = level,
RouteObjectType = RouteObjectType.Estimate,
RouteObjectId = _estimate.Id
};
Repository.Save(routeInformation);
_estimate.RouteMatrix.Add(routeInformation); // <--- The problem is here
Repository.Save(_estimate);
i++;
}
}
The problem is that, if there are multiple approvers (i.e: the length of the approvers array is greater than 1, only the first routeInformation is added in the RouteMatrix. I don't know what happen to the rest of them, but the Add method doesn't give any error.
Earlier, RouteMatrix was a public field. This problem started occuring after I made it private and encapsulated it in a public property.
Your get member returns a different list, you add to that temporary list.
get
{
if (_routeMatrix != null && _routeMatrix.Count > 0)
{
var routeMatrix = _routeMatrix.ToList(); // ToList creates a _copy_ of the list
...
return routeMatrix;
}
else return _routeMatrix;
}
.....
_estimate.RouteMatrix.Add(routeInformation); // add to the result of ToList()
I think the moral here is not to make getters too complicated. The sorting is wasted effort anyway when you just want to Add().
Also, bad things will happen when _routeMatrix == null. That may not happen but then the if (_routeMatrix != null && ...) part is misleading noise.
When you are applying ToList() then completely new list is created, which is not related to original _routeMatrix list. Well, they share same elements, but when you add or remove elements from one of lists, it does not affect second list.
From MSDN:
You can append this method to your query in order to obtain a cached
copy of the query results.
So, you have cached copy of your _routeMatrix which you are successfully modifying.
To solve this issue you can return IEnumerable instead of IList (to disable collection modifications outside of estimation class), and create AddRouteInformation method to estimation class which will add route information to _routeMatrix. Use that method to add new items:
_estimate.AddRouteInformation(routeInformation);
Repository.Save(_estimate);
The problem is that you're not actually modifying _routeMatrix, you're modifying a copy of it. Don't issue the ToList on _routeMatrix, just sort it. Change the get to this:
get
{
if (_routeMatrix != null && _routeMatrix.Count > 0)
{
_routeMatrix =
_routeMatrix.OrderBy(tm => tm.Level.LevelType).ThenBy(tm => tm.Level.LevelValue).ToList();
return _routeMatrix;
}
else return _routeMatrix;
}

Why isn't database updating?

I have the following code:
model = new Option();
model.val1 = newVal1;
model.val2 = newVal2;
model.val3 = newVal3;
//this saves new record just fine
if (recordCount < 1)
{
context.Options.AddObject(model);
context.SaveChanges();
}
else
{
var tempID = from s in context.Options where (s.val1 == newVal1 && s.val2 == newVal2) select s.ID;
var resultsID = tempID.First();
model = context.Options.Single(m => m.ID == resultsID);
if (TryUpdateModel(model, new[] { "val3" }))
{
//this isn't updating the record
context.SaveChanges();
}
}
The database adds a new entry just fine, but isn't updating it. What am I missing? Thanks.
Looking at this code, you first make a new model and set some properties on it:
model = new Option(); // <- A
model.val1 = newVal1;
model.val2 = newVal2;
model.val3 = newVal3;
then, assuming you're heading down the "else" path you do this:
var tempID = from s in context.Options where (s.val1 == newVal1 && s.val2 == newVal2) select s.ID;
var resultsID = tempID.First();
model = context.Options.Single(m => m.ID == resultsID); // <- B
if (TryUpdateModel(model, new[] { "val3" }))
{
//this isn't updating the record
context.SaveChanges();
}
which goes out and finds the entry in context.Options that has the matching ID.
So, now that model, which you created via the new() call (which I've marked with the comment "A") is now cast adrift and you've got a different one - the one you retrieved via the call to context.Options.Single(), which I've marked with the comment "B". It has properties based on what's in the context, not what was in that object you made. That A object is gone now. You've got a new object, B, retrieved from the DB.
So now, you're calling TryUpdateModel on this retrieved object, telling it that val3 is updated, but the value hasn't changed, right? It's whatever you pulled from the context.
So, it's not going to update anything because the model object isn't the one you think it is... the one you updated is waiting to be garbage collected. The one you retrieved hasn't been updated because it still has whatever value it's got for the property val3.
Assuming I follow what you're trying to do here, that's why you're not seeing any updated values in the context.
If you want to change the value of the val3 property on that model object you've retrieved, you need to set it after you retrieve it, otherwise it is overwritten.
If you are using a global context, you will have to update the context itself because it is not soft-link to the database.
context.SaveChanges();
DbContext context = new DbContext();
Check if Configuration.AutoDetectChangesEnabled = true;

Categories