I have a method that performs an insert into a table using Entity Framework 6. The table has an ID field that is populated automatically when it is written to SQL Server. I would like the method to return all of the data that was previously in the ModelState as well as the new record. When I run this code, I get everything except for the new record's ID, which comes back as a 0.
How can I rework this so it returns the new ID as well as the rest of the data from ModelState?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditingInline_Create([DataSourceRequest] DataSourceRequest request, Book book)
{
if (bookViewModel != null && ModelState.IsValid)
{
MyRepository db = new MyRepository();
db.Books.Add(book);
db.SaveChanges();
}
return Json(ModelState.ToDataSourceResult());
}
Function Add return from db result of adding with correct id parameter.
Try this:
book = db.Book.Add(book);
It give you what you want.
If you want return model and modelstate use this:
return Json(new[] { book }.ToDataSourceResult(request, ModelState);
Related
I am using entity framework core with models for all the tables in the SQL database.
I have a linq query that pulls rows from a table - let's call it facilities.
Then, I iterate the results of the query using fornext() (don't ask) :)
Within the loop we pull data from various other tables that are related to facilities.
Obviously this is a huge performance issue since there can be up to 100 rows in facilities which means the DB gets queried every time the loop iterates for every additional table we are pulling from. Note that some of the tables are from another database and you cannot join between contexts. Already tried that.
So, I thought to myself, let's pull all the rows from the related tables before we process the loop. That way, we only make those db calls one time for each associated table.
var pracloc = _ODSContext.AllPractitionerLocations
.Where(l => l.AllPractitionerLocationID != 0);
And, that works just fine.
Next step, let's simplify the code and pull some of those db calls out into private methods within the class.
For example:
Here's where I call the method (this replaces the line above).
var pracloc = GetAllPractitionerLocationsDTO();
Here's the method.
private AllPractitionerLocationsDTO GetAllPractitionerLocationsDTO()
{
AllPractitionerLocationsDTO dto = new();
dto.MyList = new List<AllPractitionerLocationDTO>();
var myo = _ODSContext.AllPractitionerLocations
.Where(s => s.AllPractitionerLocationID != 0)
.Select(g => new AllPractitionerLocationDTO()
{
AllPractitionerLocationID = g.AllPractitionerLocationID
});
dto.MyList = myo.ToList();
return dto;
}
Here's the subsequent filter (which is unchanged between the two data queries above):
var PracLocation = pracloc
.Where(a => a.LocationID = provider.LocationID)
.FirstOrDefault();
And, this works fine as long as I pull the data by querying the DB directly as in the first line above.
When I try to pull the data in the method, the line above throws:
'AllPractitionerLocationsDTO' does not contain a definition for 'Where' and no accessible extension method 'Where' accepting a first argument of type 'AllPractitionerLocationsDTO' could be found (are you missing a using directive or an assembly reference?)
AllPractitionerLocationsDTO is a model class with a subset of the rows in the "real" model:
public class AllPractitionerLocationDTO
{
public int SRCAllPractitionerLocationID { get; set; }
public int AllPractitionerLocationID { get; set; }
}
public class AllPractitionerLocationsDTO
{
public List<AllPractitionerLocationDTO> MyList;
}
Since that is identical in structure to the actual DB table, why won't the where clause work? Or, how can I implement my own where within the model class?
I even tried adding the dbset<> to the context. Still didn't work
public virtual DbSet<AllPractitionerLocationDTO> AllPractitionerLocationDTOs { get; set; }
Help me please.
You have to return IQueryable from your method. Only in this case you can reuse it later and filter effectively:
private IQueryable<AllPractitionerLocationDTO> GetAllPractitionerLocationsDTO()
{
var query = _ODSContext.AllPractitionerLocations
.Where(s => s.AllPractitionerLocationID != 0)
.Select(g => new AllPractitionerLocationDTO
{
AllPractitionerLocationID = g.AllPractitionerLocationID
});
return query;
}
I used json serialization to store list on ids in a field
Model:
public class Video
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<int> AllRelatedIds { get; set; }
}
Context:
modelBuilder.Entity<Video>(entity =>
{
entity.Property(p => p.AllRelatedIds).HasConversion(
v => JsonConvert.SerializeObject(v, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }),
v => JsonConvert.DeserializeObject<IList<int>>(v, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })
);
});
It works fine, Adding, Editing, Deleting items is easy and in SQL Database it stores as json like
[11000,12000,13000]
Everything is fine BUT!! as soon as want to query on this list I get weird responses.
Where:
_context.Set<Video>().Where(t=>t.AllRelatedIds.contains(11000)) returns null however if I ask to return all AllRelatedIds items some records have 11000 value exp.
Count:
_context.Set<Video>().Count(t=>t.AllRelatedIds.contains(11000)) returns could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
What's the matter with EF Core? I even tested t=>t.AllRelatedIds.ToList().contains(11000) but made no difference
What I should do? I don't want to have more tables, I used this methods hundreds of times but seems never queried on them.
The Json Serialization/Deserialization happens at application level. EF Core serializes the IList<int> object to value [11000,12000,13000] before sending it to database for storing, and deserializes the value [11000,12000,13000] to IList<int> object after retrieving it from the database. Nothing happens inside the database. Your database cannot operate on [11000,12000,13000] as a collection of number. To the database, its a single piece of data.
If you try the following queries -
var videos = _context.Set<Video>().ToList();
var video = _context.Set<Video>().FirstOrDefault(p=> p.Id == 2);
you'll get the expected result, EF Core is doing it's job perfectly.
The problem is, when you query something like -
_context.Set<Video>().Where(t=> t.AllRelatedIds.Contains(11000))
EF Core will fail to translate the t.AllRelatedIds.Contains(11000) part to SQL. EF Core can only serialize/deserialize it because you told it to (and how). But as I said above, your database cannot operate on [11000,12000,13000] as a collection of integer. So EF Core cannot translate the t.AllRelatedIds.Contains(11000) to anything meaningful to the database.
A solution will be to fetch the list of all videos, so that EF Core can deserialize the AllRelatedIds to IList<int>, then you can apply LINQ on it -
var allVideos = _context.Set<Video>().ToList();
var selectedVideos = allVideos.Where(t=> t.AllRelatedIds.Contains(11000)).ToList();
But isn't fetching ALL videos each time unnecessary/overkill or inefficient from performance perspective? Yes, of course. But as the comments implied, your database design/usage approach has some flaws.
I'm new to using EF to handle data in SQL. In a MVC Core project we're testing EF (Microsoft.EntityFrameworkCore, version 2.2.3) to handle data.
When trying to update data and update failed for some reason (missing fields etc) it seemed like EF actually deleted the record from the database (MSSQL 2014) instead of throwing an update error...
Is it possible?
Code for updating:
public void Update(Contact contact)
{
_dbContext.Update(contact);
_dbContext.SaveChanges();
}
When trying to update data and update failed for some reason (missing fields etc) it seemed like EF actually deleted the record from the database (MSSQL 2014) instead of throwing an update error...
Is it possible?
It should not.
test it, try to debug here
_dbContext.Update(contact);
_dbContext.SaveChanges();
var updated = _dbContext.Contacts.FirstOrDefault(x => x.Id == contact.Id); //debug here
check if it has a value, if still none, these are the scenarios i can think of that may have caused your problem
investigate the missing field specially if it is not nullable.
is the _dbContext used here is the same connection string used with everything?
is the [Key] attribute listed on your Contact entity?
public class Contact
{
[Key]
public int Id
}
overridden the SaveChanges function?
is what you are passing Contact contains a Key and it is not 0?
is a delete function called after Update?
try using SQL Profiler to look at the Linq to SQL if it really generated an update query and if it is really pointing at the right [Key]
but if it is still not working properly, you could do
public void Update(Contact contact)
{
var selectedContactToBeUpdated = _dbContext.Contacts.FirstOrDefault(x => x.Id == contact.Id);
if (selectedContactToBeUpdated != null)
{
selectedContactToBeUpdated.PropertyToBeUpdated1 = newValue;
selectedContactToBeUpdated.PropertyToBeUpdated2 = newValue2;
//additional Properties
_dbContext.SaveChanges();
}
}
in the scenario above, it will only generate an Update statement with fields you have changed.
Ok, first off I have seen several questions on this issue. I have gone through just about all of them and none of the solutions prescribed worked for me.
I am using asp.net core 1.1.0 and SQL Server 2016. I am also using the code first approach. The following is my mappings for the table in question
public class TierMap : IEntityTypeConfiguration<Tier>
{
public void Map(EntityTypeBuilder<Tier> builder)
{
builder.HasKey(a => a.TierID);
builder.Property(a => a.TierID).UseSqlServerIdentityColumn<int>();
builder.HasOne(a => a.Country)
.WithMany(a => a.Tiers)
.HasForeignKey(a => a.CountryID);
}
}
Below is the design view of the table in SQL Server:
When I create a new instance of a Tier object, all the properties except the TierID get some value (a random negative integer). So inserting into the database throws the following error
Cannot insert explicit value for identity column in table 'Tiers' when IDENTITY_INSERT is set to OFF.
Below is the code the attempts to save the data
[HttpPost("AddTier")]
public async Task<IActionResult> AddTier([FromBody]TierDataModel model)
{
if (!ModelState.IsValid)
{
Response.AddApplicationError("Data is invalid ");
return BadRequest();
}
Tier tier = new Tier();
tier.TierName = model.TierName;
tier.TierNo = model.TierNo;
tier.CountryID = model.CountryID;
_tierService.AddTier(tier);
await _unitOfWorkAsync.SaveChangesAsync();
return Ok();
}
Changing _dbSet.Attach(entity); to _dbSet.Add(entity); in my Repository Class solved the problem
I am developing code for app engine. I tried to update an existing row by updating the same entity returned as result of query. But it creates new row instead of updating the same row. Following is the code:
public boolean updateProfile(DbProfile profile) {
Transaction txn = _datastore.beginTransaction();
Entity entity = getProfileEntity(profile.getLoginId());
if (entity != null) {
entity.setProperty(DbProfile.DbProfilePropertyNames.address, profile.getAddress());
entity.setProperty(DbProfile.DbProfilePropertyNames.name, profile.getName());
Key key = _datastore.put(entity);
txn.commit();
return true;
}
return false;
}
private Entity getProfileEntity(String userName) {
Key eRecommendationKey = KeyFactory.createKey("eRecommendation", _dbKeyName);
FilterPredicate predicateUsername =
new FilterPredicate(DbProfile.DbProfilePropertyNames.loginId, FilterOperator.EQUAL,
userName.toUpperCase());
Query query =
new Query(DbProfile.entityProfileName, eRecommendationKey).setFilter(predicateUsername);
List<Entity> profiles =
_datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
Utils.log.log( Level.SEVERE, "not found"+profiles.size() );
if (profiles.size() == 0) {
//profile data is not set yet
return null;
} else {
return profiles.get(0);
}
}
Following image shows fields in the entity.
Please let me know how can I fix the issue.
My Java skills are not too good, so I find it difficult to understand your code sample. I also don't see where updateProfile() is called and how your code is getting or constructing the profile object, especially whether the key of the profile object is altered.
But in general, if new entities are created instead of updating existing entities, the reason is that the key at your updating commit is different from the actual key of the existing entity.
Typical situations:
a string (key-name) is used instead of an integer (ID), or vice versa
a typo in the kind name of the key
different namespace or app properties of the key
parents are missing in the key path or are constructed wrongly
Suggestion:
In datastore viewer, compare the key of an existing entity with the key of the accidentally created entity. The difference between both keys might give you a hint where to look in your code for the bug.
I solved the problem. It was my mistake. I had called saveData servlet instead of updateProfile servlet.