EntityFramework Index Insert at least one - c#

couldn't think of any better title for my question, but the situation is really simple: I have my database (created using code first migration), I have my entity, it has two fields that has index contraint so I cannot insert duplicate records when those fields are same.
public class Entity
{
public int Id;
[Index("IndexName"), IsUnique = true, Order = 0]
[MaxLength(50)]
public string Name;
[Index("IndexName"), IsUnique = true, Order = 1]
public Guid CustomId;
}
For a short entity class example I made this (mine looks with more properties, getters, setters ;) )
I try to make this:
<...>
using (var ctx = new EntitiesDbContext())
{
var e1 = new Entity()
{
Name = "Bob",
CustomId = new Guid("068462F1-3557-E711-BA31-028037EC0200")
};
var e2 = new Entity()
{
Name = "Bob",
CustomId = new Guid("068462F1-3557-E711-BA31-028037EC0200")
};
ctx.Entities.Add(e1);
ctx.Entities.Add(e2);
await ctx.SaveChangesAsync(); // I get exception
}
<...>
Everything is fine, I cannot insert two records because of index, but my problem is, that it does not insert any value at all. Is it possible to make this situation, to add at least one value to DB (i.e. e1 object) ?
P.s. The problem came from more complex situation, my example is pretty obvious or stupid, but it shows the idea what I want to achieve. The problem was the system performance I guess, when two records were inserted into context and then when I context tried to save it, I got an exception.

In all versions of Entity Framework, whenever you execute
SaveChanges() to insert, update or delete on the database the
framework will wrap that operation in a transaction.
Source: https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx
So you have to save every item seperatly because the behaviour of a transaction is to do no changes to your database in case of an error.
ctx.Entities.Add(e1);
ctx.SaveChanges();
ctx.Entities.Add(e2);
ctx.SaveChanges();

What I found interesting, I can check ctx.ChangesTracker object to get entities list that are changed.
So I just did simple check for changes count and if the count was 2 for example, I made those entities state to Unchanged.
ctx.ChangeTracker.Entries().First().State = EntityState.Unchanged
This solved my problem and pain that I was dealing with for several hours.

Related

Assigning entity instance instead of entity id creates new record

I have these two tables:
public class FiscalYear
{
... other fields
public int FiscalYears_Id { get; set; }
}
public class SkipHeader
{
... other fields
public int FiscalYears_Id { get; set; }
public virtual FiscalYear FiscalYear { get; set; }
}
Attempting to create a new SkipHeader like so:
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYear = Session.FiscalYear,
}
Will cause the database to create a new FiscalYear record instead of using the Session.FiscalYear which is simply a static property that gets assigned to at program start. However, if I assign the FiscalYears_Id instead:
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYears_Id = Session.FiscalYear.FiscalYears_Id,
}
The program uses the existing record as expected.
This bug eluded me and my colleague for months! Now that I found a solution, I would like to know WHY this is the case?
This bug eluded me and my colleague for months! Now that I found a
solution, I would like to know WHY this is the case?
This occurs because the DbContext doesn't know about your FiscalYear object instance, such as whether it represents a new record or an existing one.
Take the following example:
var fiscalYear = new FiscalYear { Id = 4, Name = "2019/20" };
var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
context.SkipHeaders.Add(skipHeader);
context.SaveChanges();
fiscalYear in this instance is an object instance that has been given an ID and Name. When we associate it to a new SkipHeader and add the SkipHeader to the DbContext, EF will see this fiscalYear. Since it isn't an object tracked by the context, it treats it as a new entity like the SkipHeader.
Depending on how your entities are configured for dealing with the PK will determine what happens.
If your PK (Id) is set up as an Identity column (DB will populate) then the FiscalYear will be inserted and assigned the next available Id value. After the SaveChanges() call, fiscalYear.Id would be "6" or "22" or whatever the next new ID assigned to it would be. (Not "4")
If your PK is not an Identity column (App will populate) and a FiscalYear row already exists in the DB for ID 4, then EF will throw a duplicate key Exception on SaveChanges().
Where people get confused is that they assume that since the FiscalYear was at one point (Say during a web request) loaded from a DbContext, it is still somehow acting as a tracked entity when passed into another method outside of the scope of that DbContext. (During another update web request) It's not. When a web request for instance accepts a FinancialYear as a parameter from the client, it is deserializing a FinancialYear. As far as EF is concerned, that is absolutely no different than the new FinancialYear { } example above. The DbContext is not aware of that entity.
Take the following example:
FiscalYear fiscalYear = null;
using (var context = new AppDbContext())
{
fiscalYear = context.FiscalYears.Single(x => x.Id == 4);
}
using (var context = new AppDbContext())
{
var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
context.SkipHeaders.Add(skipHeader);
context.SaveChanges();
}
This provides a basic outline of a Fiscal Year that was loaded by one instance of a DbContext, but then referenced by another instance of a DbContext. When SaveChanges is called, you will get a behaviour like you are getting now. This is what essentially happens in web requests, as when an entity is returned, the entity definition is merely a contract and the Entity is serialized to send to the client. When it comes back into another request, a new untracked object is deserialized.
As a general rule, Entities should not be passed outside the scope of the DbContext they were read from. EF does support this via detaching and re-attaching entities, but this is honestly more trouble than it is typically worth because you cannot 100% rely on just attaching an entity using DbContext.Attach() as often there can be conditional cases where another entity instance is already being tracked by a context and the Attach will fail. In these cases you'd need to replace references with the already tracked entity. (Messy conditional logic to catch possible scenarios) References are everything when dealing with EF. Two different object references with the same key & values are treated as separate and different objects by EF. Rather than passing references around, it's usually a lot simpler, and better to pass just the FK. This has the benefit of being a smaller payload for web requests.
One option you've found out is to update via the FK:
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYears_Id = Session.FiscalYear.FiscalYears_Id,
}
This works, however when you have entities that are exposing both FK (FiscalYears_Id) and navigation property (FiscalYear) you can potentially find mismatch scenarios when updating records. This is something to be careful with as an application evolves.
For instance, take an example where you are editing an existing SkipHeader with a FiscalYears_Id of "4". This will have an associated FiscalYear reference available with a PK of "4".
Take the following code:
var skipHeader = context.SkipHeaders.Include(x => x.FiscalYear).Single(x => x.Id == skipHeaderId);
skipHeader.FiscalYears_Id = newFiscalYearId; // update FK from "4" to "6"
var fiscalYearId = skipHeader.FiscalYear.Id; // still returns "6"
context.SaveChanges();
We set the FK value on the skip header, however that does not update the reference for FiscalYear until after we call SaveChanges. This can be an important detail when dealing with FKs alongside navigation properties. Now normally we wouldn't bother going to the Navigation Property to get the ID again, but any code we call that is expecting the new FiscalYear reference to be updated will have a different behavior depending on whether SaveChanges had been called before or after the code in question. If before, all FiscalYear details will be for the old fiscal year even though we changed the FK reference.
This can also lead to odd Lazy Loading errors as well such as:
var skipHeader = context.SkipHeaders.Single(x => x.Id == skipHeaderId);
skipHeader.FiscalYears_Id = newFiscalYearId; // update FK from "4" to "6"
var fiscalYearId = skipHeader.FiscalYear.Id; // NullReferenceException!
context.SaveChanges();
Normally, provided you have lazy loading enabled loading a SkipHeader without eager loading the FiscalYear (.Include(x => x.FiscalYear))and then querying a property from the FiscalYear would lazy load this relative. However, if you change the SkipHeader's FiscalYear_ID FK and then try to access a property off the FiscalYear before calling SaveChanges(), you will get a NullReferenceException on the FiscalYear. EF will NOT lazy load either the old or new FiscalYear entity. Bugs in behaviour like that commonly creep in as applications get developed and code starts calling common functions that assume they are dealing with complete entities.
The alternative to setting updated values for known rows by FK is to load the entity to associate, and associate it by reference:
using (var context = new AppDbContext())
{
var fiscalYear = context.FiscalYears.Single(x => x.Id == fiscalYearId);
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYear = fiscalYear;
}
context.SaveChanges();
}
This example just uses a locally scoped DbContext. If your method has an injected context then use that instead. The context will return any cached, known instance of the Fiscal Year or retrieve it from the DB. If the FiscalYear ID is invalid then that operation will throw an exception specific to the Fiscal Year not being found due to the Single() call rather than a more vague FK violation on SaveChanges(). (Not an issue when there is only one FK relationship, but in entities that have dozens of relationships...)
The advantage of this approach is that the FiscalYear will be in the scope of the DbContext so any methods/code using it will have a valid reference. The entities can define the navigation properties without exposing the extra FK values,using .Map(x => x.MapKey()) [EF6] or Shadow Properties [EFCore] instead to avoid 2 sources of truth for FK values.
This hopefully will provide some insight into what EF is doing and why it resulted in the behaviour you've seen and/or any errors or buggy behaviour you might have also come across.
Assuming you have pretty standard setup with DbContext being scoped (per request) dependency - the reason is that the new instance of your DbContext does not track the Session.FiscalYear instance - so it creates new. Another way to solve this is using DbContext.Attach:
context.Attach(Session.FiscalYear);
var skipHeader = new SkipHeader()
{
... other fields get assigned to
FiscalYears_Id = Session.FiscalYear.FiscalYears_Id,
}
// save skipHeader
More about change tracker in EF.

How to deal with multiple newly Added Entities that depend on each otter with a Foreign key constraint - EF6

I had a bit of difficulties with getting the right title for this problem so I hope my explanation below will make it a bit clearer.
I am using EntityFramework 6 and I am doing multiple inserts within a function.
There are 3 different tables which get updated / inserted: table EntityMethod, EntityRoom and EntityRoomMethod. The table EntityRoomMethod has a foreign key relationship with the table EntityMethod.
In some cases, a EntityMethod row is missing and this is newly created by adding the object with entity framework:
if (mn == null)
{
mn = new Method
{
ElementId = floorProgram.ElementId,
ActionId = m.ActionId,
ElementCount = m.ElementCount,
ColorId = m.ColorId,
IsBase = m.IsBase,
IsHccp = m.IsHccp,
TimeNorm = m.TimeNorm,
Frequency5Id = m.Frequency5Id,
MaterialId = m.MaterialId,
ProductId = m.ProductId,
MethodTypeId = m.MethodTypeId,
};
}
In another part of code the Method foreign key (MethodId) of the EntityRoomMethod table is being set:
roomMethodObject.RightId = mn.Id;
RightId is in this case the relationship with the EntityMethod table.
On a later point the other 2 table objects (EntityRoom and EntityRoomMethod) are also added (DBSet.Add) using EF.
The problem however is, that when the EntityMethod is newly added, it gets the Id value of 0, because SaveChanges() is not yet executed. The foreign key reference in the EntityRoomMethod is therefor also being set to 0.
When the function returns to the caller, the SaveChanges() is being executed and all 3 objects (representing the 3 tables) are being saved.
This however will generate a FK error (because Id 0 does not exist obviously).
I tried to fix this by calling SaveChanges() after Adding the new Method (so directly in the function). This however will cause some other problems.
In the end I have gotten multiple errors but I assume it all has to do with the same thing, the errors were the following:
Unable to determine the principal end of the 'Solution.Data.RoomMethod_Method' relationship. Multiple added entities may have the same primary key.
The property 'RightId' is part of the object's key information and cannot be modified.
Conflicting changes to the role x of the relationship y have been detected
So now the actual question:
Is there an (easy) way to call SaveChanges() after all 3 entities have been added with EF but also handling the FK errors? Does this mean I have to generate the Id's myself? Or was the first approach better (Calling SaveChanges directly after adding the EntityMethod object).
For now I have some not-nice-looking solution with doing a direct INSERT statement after adding a new EntityMethod (using Dapper). This kind-of works but I assume there is a better way wherein I can just use EF6.
P.S. calling SaveChanges() after adding the EntityMethod was basically the same by doing it with Dapper, however this generated some other errors while using Dapper it didn't generate those errors.
You can wrap several SaveChanges within a transaction, and EF (or probably SQL) will generate the required ids at each point, allowing you to reference them later on.
using(var transaction = _context.Database.BeginTransaction())
{
var p1 = new Something { Name = "Fred" };
_context.SaveChanges();
var a2 = new Dependency { SomethingId = p1.Id }; <-- p1.Id now has an Id value
_context.SaveChanges();
var b3 = new OtherDependency { DependencyId = a2.Id }; <-- a2.Id now has an Id value
_context.SaveChanges();
transaction.Commit(); <-- All 3 changes are fully committed to the db at this point.
}

Insert order of Entity Framework children

I have a structure like this
public class Son {
public string Name {get;set;}
public int Age {get;set;}
}
public class Daughter {
public string Name {get;set;}
public int Age {get;set;}
}
public class Parent {
public Daughter[] Daughters {get;set;}
public Son[] Sons {get;set;}
}
Where there is a FK Parent -> Son and Parent -> Daughter
Currently when doing a Context.SaveChanges() on a parent object it saves the Parent, and then saves the Daughters and then saves the Sons. I need it to save the Sons before the Daughters because we have a database trigger that does validation of the Sons based on the Daughters (and will deny the whole thing if it doesnt meet a requirement)
This trigger is obviously outside the knowledge of EF.
How can I specify that Sons are dependent on Daughters in EF such that Sons get inserted first; or is there a specification or attribute that I can define insert order?
PS: Do not look too much into the contrived example (such as why we dont save it under one thing called Children). The real-world example is much more complicated but the idea of saving Sons before Daughters is there
I love a challenge!
Firstly a declaration: I'm not a fan of Triggers or building a requirement for making order of insert important. My first exercise would be to exhaust all options to remove such a requirement.
After a bit of tinkering from what I can see at least when adding entities, for instance a Parent with one or more Daughters and one or more Sons, the order of insert is consistently alphabetical based on the Entity names. For example with entities named "Parent", "Daughter", and "Son", the insert was always Parent > Daughter > Son. The order of the properties, configuration, inserts, or even the table names had no bearing on the operations, however renaming the entity class "Son" to "ASon" resulted in Sons being inserted before Daughters. I don't know if this will carry forward to edits, but it's something to consider without getting too hacky. (Though something like this would definitely need to be documented well in the system in case someone questions a naming convention to get something inserting before something else.)
That said, getting into the hacky fun business!
Using a Son entity called ASon to force Sons before Daughters, it is possible to get EF to reverse that insert order:
using (var context = new ParentDbContext())
{
var parent = context.Parents.Create();
parent.Name = "Steve";
parent.Daughters.Add(new Daughter { Name = "Elise" });
parent.Daughters.Add(new Daughter { Name = "Susan" });
parent.Sons.Add(new ASon { Name = "Jason" });
parent.Sons.Add(new ASon { Name = "Duke" });
context.Parents.Add(parent);
context.SaveChanges();
}
Out of the box this inserted Parent, Son, Son, Daughter, Daughter.
To reverse it, I overrode SaveChanges, looking for our Sons to defer saving until after everything else:
public override int SaveChanges()
{
var trackedStates = new[] { EntityState.Added, EntityState.Modified };
var trackedParentIds = ChangeTracker.Entries<Parent>().Where(x => trackedStates.Contains(x.State)).Select(x => x.Entity.ParentId).ToList();
var addedSons = ChangeTracker.Entries<ASon>().Where(x => x.State == EntityState.Added).ToList();
var modifiedSons = ChangeTracker.Entries<ASon>().Where(x => x.State == EntityState.Modified).ToList();
int tempid = -1;
int modifiedParentCount = addedSons.Select(x => x.Entity.Parent.ParentId)
.Where(x => trackedParentIds.Contains(x))
.Count();
List<Tuple<Parent, ASon>> associatedSons = new List<Tuple<Parent, ASon>>();
modifiedSons.ForEach(x => { x.State = EntityState.Unchanged; });
addedSons.ForEach(x =>
{
x.Entity.SonId = tempid--;
associatedSons.Add(new Tuple<Parent, ASon>(x.Entity.Parent, x.Entity));
x.Entity.Parent.Sons.Remove(x.Entity);
x.State = EntityState.Unchanged;
});
var result = base.SaveChanges();
addedSons.ForEach(x => { x.Entity.Parent = associatedSons.Single(a => a.Item2 == x.Entity).Item1; x.State = EntityState.Added; });
modifiedSons.ForEach(x => { x.State = EntityState.Modified; });
result += base.SaveChanges() - modifiedParentCount;
return result;
}
So what this is doing:
The first bit is easy, we find our added and modified sons. We also take a count of parents with both modified and added sons. These will get double-counted when this is done.
For modified sons, we just set their state to Unchanged.
For added sons, we need to do a bit of dirty work. We need to give them a temporary unique ID because to mark them as Unchanged, EF still wants to track their ID and when you add 2 sons it will fail here. Note that when we put them back as added, they will receive proper IDs from Identity columns, not these temporary negative ones. We also track the association of their added sons with their respective parent in a Tuple because we need to temporarily remove those sons from their parent. Finally the added son is also marked unchanged.
Now we call the base SaveChanges which will save our parents and their daughters.
For modified sons, we just need to update the state back to Modified.
For our added sons, we use our saved association to re-assign them to their parent, and then set their state back to added.
We call the base SaveChanges again, appending the affected row count to the first run, and subtract the duplicate parent references. (parents that were already counted due to being modified)
The sketchy bit is adjusting the result count for the double-save, this might not be 100% accurate but it should only be an issue if you happen to be using the result of SaveChanges. I can't say I've ever really paid much attention to that return value :)
Hopefully that gives you some ideas to play with.

How do I get an identity value with Entity Framework(v5) before I save the record

I am new to entity framework and I have been searching a while for an answer to this question and I can't find anything that directally addresses this.
Here is the problem. I have a table in Oracle. In this table there are 2 fields(there are more but not important to this question). Card_Transaction_Id and Parent_Card_Transaction_ID. The Parent_Card_Transaction_Id field is constrained by the Card_Transaction_Id field and I am using a Oracle sequence via a trigger to populate the Card_Transaction_Id field.
In my code, I am using Entity Framework(Version 5) to connect using the Code First Approach.
The issue is when I try to create a new record. I need to know what the next sequence value is in order to populate the Parent_Card_Transaction_Id. My mapping for card transactions:
public class CardTransactionMap : EntityTypeConfiguration<CardTransaction>
{
public CardTransactionMap(string schema)
{
ToTable("CARD_TRANSACTION", schema);
// Mappings & Properties
// Primary Key
HasKey(t => t.CardTransactionId);
Property(t => t.CardTransactionId)
.HasColumnName("CARD_TRANSACTION_ID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(t => t.ParentCardTransactionId)
.HasColumnName("PARENT_CARD_TRANSACTION_ID");
Property(t => t.CardProfileId)
.HasColumnName("CARD_PROFILE_ID");
}
}
The question is - is there any way to get the next sequence number before I save the record?
My current work arround is to use the following method:
public static decimal GetNextCardTransactionSequenceValue()
{
using (var context = new Context(new OracleConnectionFactory().GetConnection()))
{
var sequence = context.Database.SqlQuery<int>("select card_transaction_id from card_transaction").ToList();
return sequence[0];
}
}
Using that method, I get the next value and then just populate my table. This works but I don't like doing it this way. I feel that there must be a better way to do it.
Thanks in advance.
You have to do this by navigation properties.
By fetching the next value from a sequence before actually using it in the same session you create yourself a concurrency issue: another user can increment the index (by an insert) in the time between drawing its next value and assigning it to the child record. Now the child will belong to the other user's record!
If your CardTransaction class has a parent reference like this:
int ParentCardTransaction { get; set; }
[ForeignKey("ParentCardTransaction")]
CardTransaction ParentCardTransaction { get; set; }
you can create a parent and child in one go and call SaveChanges without worrying about setting FK values yourself:
var parent = new CardTransaction { ... };
var child = new CardTransaction { ParentCardTransaction = parent, ... };
SaveChanges();
Now EF wil fetch the new CardTransactionId from the parent and assign it to the FK of the child. So generating and getting the parent Id happens all in one session, so it is guaranteed to be the same value.
Apart from preventing concurrency issues, of course it is much easier anyway to let EF do the heavy lifting of getting and assiging key values.
Create a Stored Procedure or Query that will return you the next Value from the Table here is an Example
SELECT NVL(MAX(card_transaction_id + 1), 0) AS MAX_VAL
FROM card_transaction T;
or Create a Trigger - for OracleDB
Change your table definition to this :
CREATE TABLE t1 (c1 NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
c2 VARCHAR2(10));
as per the information in the link i provided in the comment.
after the update ef will automatically query the value for the id that is inserted, there is no need to fill in the id before the insert. ef will generate an insert sql query without id.

Why entity framework isn't implement identity map with unit of work?

I have written test code as bellow:
Entities db = new Entities();
var place = new Place
{
Id = Guid.NewGuid(),
Name = "test",
Address = "address"
};
db.Places.Add(place);
var cachedPlace = db.Places.Where(x => x.Id == place.Id).FirstOrDefault(); \\ null
I expected dbset will return the added entity. But it gives me object only after changes were saved to the real DB.
If you want to access the unsaved query, then you use the Local property of the DbSet.
The reason it doesn't work the way you want is that it must also support autonumbered identities, and that will mean the ID is 0. If you insert multiple records, you would have multiple objects with the same 0 ID. EF won't know what the real ID is until after it's been saved.

Categories

Resources