I have a database first model.
My application UI provides a group of checkboxes, one for each value in Data_Type.
When the user checks one, I expect a row to be added in BUS_APPL_DATA_TYPE,
however I'm getting an error about Cannot insert explicit value for identity column in DATA_TYPE (And I absolutely do not actually want to insert data in this table)
My EF Model class for BUS_APPL has this property
public ICollection<BusApplDataType> BusApplDataType { get; set; }
And that EF Model class looks like
public partial class BusApplDataType
{
public int BusApplId { get; set; }
public int DataTypeId { get; set; }
[Newtonsoft.Json.JsonIgnore]
public BusAppl BusAppl { get; set; }
public DataType DataType { get; set; }
}
What exactly do I need to add to the BusApplDataType collection to get a record to be inserted in BUS_APPL_DATA_TYPE?
Edit:
At a breakpoint right before SaveChanges.
The item at index 2 is an existing one and causes no issues.
The item at index 3 is new. Without this everything updates fine. There is a DATA_TYPE with id 5 in the database.
The surrounding code, if it helps.
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] BusAppl item)
{
...
var existing = _context.BusAppl.FirstOrDefault(t => t.Id == id);
...
existing.BusApplDataType = item.BusApplDataType; //A bunch of lines like this, only this one causes any issue.
...
_context.BusAppl.Update(existing);
_context.SaveChanges();
return new NoContentResult();
}
My issue was that I needed to use my context to look up the actual entity, using info passed, instead of using the one with all the same values that was passed into my api directly.
Related
public partial class Table
{
public int TableId { get; set; }
public Nullable<int> Num { get; set; }
public int NbSeats { get; set; }
public Nullable<int> R_LocationId { get; set; }
public virtual ICollection<Availibility> Availibilities { get; set; }
public virtual R_Locations R_Locations { get; set; }
}
public partial class Availibility
{
public System.DateTime DayRes { get; set; }
public Nullable<bool> Available { get; set; }
public int TableId { get; set; }
public virtual Table Table { get; set; }
}
I would like to implement this request with entity Framework:
select *
from Tables join Availibilities
on Availibilities.TableId = Tables.TableId
where Availibilities.Available=1
One of the slower parts of fetching data from a database is the transport of the data from the DBMS to your local process. Hence you should not transport more values than you actually plan to use.
Your Table has zero or more Availabilities. Your database implements this by giving the Availability table a foreign key to the Table that it belongs to.
So if you have a table with Id 4, which has 100 Availabilities, and you would query the Table with its Availabilities using a Join and Include you would transfer the foreign key Availability.TableId a 100 times, while you already know they will all have the value of Table.Id. You even know this value, because you asked for table with Id 4.
Hence, unless you plan to Update retrieved values, always use Select instead of querying complete classes.
Back to your question
Given a tableId, you want information of the table together with (some or all) its Availabilities.
Thanks to entity framework you don't have to use Join. If you use the collections, entity framework will do the join for you.
var tableWithAvailabilities = myDbContext.Tables
.Where(table => table.TableId == tableId)
.Select(table => new
{
// select only the properties you plan to use
Id = table.TableId,
Num = table.Num,
...
Availabilities = table.Availabilities
.Where(availability => ...) // if you don't want all Availabilities
.Select(availability => new
{
// again: select only the properties you plan to use
// not needed, you know it equals Table.TableId
// Id = availability.TableId,
Date = availability.DayRes,
Available = availability.Avialable,
})
.ToList(),
});
Entity framework knows which Join is needed for this. One of the nicer things when using the Collections instead of a Join is that you make your database more abstract: you really are thinking of a Table that has zero or more Availabilities. It is a shortage of a DBMS that it needs two tables to implement this. If your internal tables changes, for instance the name of the foreign key, your query does not change, because you don't use the foreign key
If you are not planning to update a fetched value, then it is seldom wise to fetch
There is two way to filter include Entity.
Using a projection (See #Harald answer)
Using a third party library
Disclaimer: I'm the owner of the project Entity Framework Plus
(
The EF+ Query IncludeFilter allow easily filter included entities. It works with EF6.x
return context.Tables
.IncludeFilter(x => x.Availibilities .Where(c => c.Available == 1))
.ToList();
Under the hood, the library does exactly a projection.
One limitation is all Include but now be called with IncludeFilter even if no filter is specified such as the account.
Wiki: EF+ Query Include Filter
I am trying to use EF Code First on an existing database. I first tried some of the reverse-engineering tools, but I ran into problems with that, so at the moment I am trying to hand-code some of the classes. I am having some trouble getting some of the foreign key relationships set up. Consider two tables. The first is called LocaleValueLookup:
public class LocaleValueLookup
{
public int Id { get; set; }
public Guid Guid { get; set; }
}
This table provides an Id for multi-language text held in a different table (that other table is not important for the purposes of this question). The second table is called SectionType, and it has an optional FK to LocaleValueLookup:
public class SectionType
{
public int EnumId { get; set; }
public string Name { get; set; }
public int? DefaultSectionTextLocaleValueLookupId { get; set; }
// Navigation property
public LocaleValueLookup DefaultSectionTextLocaleValueLookup { get; set; }
}
I have tried various things, including adding a [ForeignKey] attribute to the SectionType.LocaleValueLookup property, and various incantations in the DbContext.OnModelCreating() override, but when I query the DbContext, I can't get the DefaultSectionTextLocaleValueLookup to be anything but null. I can retrieve other objects from the context just fine, and I have verified that DefaultSectionTextLocaleValueLookupId is not null at least some of the time.
My OnModelBuilding() contains the following:
modelBuilder.Entity<LocaleValueLookup>()
.ToTable("LocaleValueLookup")
.HasKey(lvl => lvl.Id);
modelBuilder.Entity<LocaleValueLookup>().Property(lvl => lvl.Id).IsRequired();
modelBuilder.Entity<SectionType>()
.ToTable("SectionType")
.HasKey(st => st.EnumId);
modelBuilder.Entity<SectionType>().Property(st => st.EnumId).IsRequired();
A couple of other points:
I would prefer not to have a SectionType collection on the LocaleValueLookup object. LocaleValueLookup is a low-level class that a lot of other classes depend on, so to include a collection property on LocaleValueLookup for every other class that references it will make for an unwieldy class with a lot of collections on it that I don't need from a domain perspective.
I would prefer to do the mapping setup in DbContext.OnModelCreating() rather than using attributes on my model objects
Any help would be greatly appreciated!
It looks like your foreign key is nullable so that means an optional -> many relationship.
Could you try something like this:
modelBuilder.Entity<SectionType>()
.HasOptional(opt => opt.DefaultSectionTextLocaleValueLookup)
.WithMany() // no navigation on the other side
.HasForeignKey(fk => fk.DefaultSectionTextLocaleValueLookupId);
If you were to write a query like this you should get a value back:
var query =
from st in db.SectionTypes
where st.EnumId == 12345
select new
{
SectionType = st,
LocaleValue = st.DefaultSectionTextLocaleValueLookup
};
It will only be non-null if the foreign key has a value, obviously.
According to msdn article, the following should create an optional:optional relationship, but instead it creates optional:many relationship. Is the article wrong?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Optional_1>()
.HasKey(o1 => o1.id1);
modelBuilder.Entity<Optional_2>()
.HasKey(o2 => o2.id2);
modelBuilder.Entity<Optional_1>()
.HasOptional(o1 => o1.Dependent)
.WithOptionalPrincipal(o2 => o2.Principal);
}
public class Optional_1
{
public int id1 { get; set; }
public Optional_2 Dependent { get; set; }
}
public class Optional_2
{
public int id2 { get; set; }
public Optional_1 Principal { get; set; }
}
thank you
The table might look like one to many, but Entity Framework will enforce it as optional:optional because of the navigation properties. Since the navigation property is only a single object and not a collection, there is no way to add multiple.
If you look at the generated tables, it creates a nullable foreign key to your principal table (Optional_1). This allows you to create an Optional_2 that is not associated with an Optional_1.
If you were to insert multiple rows into Optional_2 that have the same foreign key to Optional_1 outside of EF, there wouldn't be anything preventing it from going through. If you were to try and load these entities you would get an error. You can't add a unique index to the column because it needs to allow NULL since it is optional.
I'm using following class to insert products to database.
ID column is primary key.
After adding multiple products to db context (without calling savechanges method) all newly added rows identity columns are zero!
My scene...
User adds several products and browse them on the data grid.
User selects one product and adds some barcodes to selected product.
When user finishes the job clicks on save button and application calls SaveChanges method!
When user wants to add some barcodes to products firstly I need to find selected product from context and adds entered barcode text to Barcodes list. But I cant do that because all products identity columns value are the same and they are zero.
How can I solve this problem?
public class Product
{
public int ProductID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public virtual List<Barcode> Barcodes { get; set; }
}
public class Barcode
{
public int BarcodeID { get; set; }
public string BarcodeText { get; set; }
public int ProductID { get; set; }
public virtual Product Product { get; set; }
}
Identity column value is assigned by database when you are inserting record into table. Before you call SaveChanges no queries are executed, nothing is passed to database and back. Context just keeps in-memory collection of entities with appropriate state (state defines which time of query should be executed during changes saving - for new entities, which have Added state, insert query should be generated and executed). So, ID stays with its default value, which is zero for integer. You should not give value manually. After inserting entities into database, context will receive ID value and update entity.
UPDATE: When you are adding Barcode to existing product, then EF is smart enough to update keys and foreign keys of entities:
var product = db.Products.First(); // some product from database
var barcode = new Barcode { BarcodeText = "000" };
// at this point barcode.ID and barcode.ProductID are zeros
product.Barcodes.Add(barcode);
db.SaveChanges(); // execute insert query
// at this point both PK and FK properties will be updated by EF
I am using EF 4.1 and lazy loading. I have below entities:
public abstract class PersonBase
{
[Key(), Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
....
[ForeignKey("Quantity")]
public virtual int? QuantityId { get; set; }
public virtual Quantity Quantity { get; set; }
....
}
public class ConcretePerson : PersonBase
{
....
}
public class Quantity
{
[Key(), Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public virtual float QuantityA { get; set; }
[Required]
public virtual float QuantityB { get; set; }
[Required]
public virtual float QuantityC { get; set; }
}
IDbSet<Quantity> Quantities;
IDbSet<ConcretePerson> ConcretePersons;
IDbSet<PersonBase> Persons;
so in my code I perform below:
using (DataBaseContext context = new DataBaseContext())
{
IQueryable<ConcretePerson> concretePersonCollection = context.ConcretePersons.Where(<condition>);
foreach (ConcretePerson concretePerson in concretePersonCollection)
{
...
concretePerson.Quantity.QuantityA = new_quantity_A;
concretePerson.Quantity.QuantityB = new_quantity_B;
concretePerson.Quantity.QuantityC = new_quantity_C;
...
}
...
DbEntityEntry<ConcretePerson> entityEntry;
Quantity quantity;
foreach (ConcretePerson concretePerson in concretePersonCollection)
{
entityEntry = context.Entry<ConcretePerson>(concretePerson);
if (entityEntry.State == System.Data.EntityState.Modified)
{
quantity = ((ConcretePerson)entityEntry.CurrentValues.ToObject()).Quantity;
}
else
{
quantity = concretePerson.Quantity;
}
...
}
...
context.SaveChanges();
}
Note that I only perform SaveChanges at the end so database is not updated until this point is reached.
I have problems within the second foreach:
1.- When entityEntry.State is modified it happens that ((ConcretePerson)entityEntry.CurrentValues.ToObject()).Quantity is null but
((ConcretePerson)entityEntry.CurrentValues.ToObject()).QuantityId is correct (contains the correct value)
Why? How to get this different from null with the current values (neither original values nor database values), just current values?
2.- If I check directly the Quantity by performing concretePerson.Quantity it is not null but
concretePerson.Quantity contains the current values (the ones updated in the first foreach),
not the original ones (the values before updating in the first foreach). Should not concretePerson.Quantity
contain the original values (before updating in the first foreach) instead? because I have not
performed any context.savechanges between the two foreach loops.
3.-Context.SaveChanges is not saving the changes done to the database and is not raising any error. <---- This point is solved, I was pointing to a different context, no using the same (now I am using the same).
That is an odd way of trying to get values... try this.
concretePerson.Quantity will be the local copy of the entity so it will have whichever value you assigned to it.
In the first foreach you are actually modifying each of the items in the collection (even if it is not saved to the database yet it is still in memory, otherwise how would EF know what to save to the database?).
In the second you are actually checking the same collection to see if entities have been modified (which they have) and then getting the current value. However the current value for quantity will be the same as .quantity because you have modified the entity. If you check the original value for modified entries you will see that it is different.
Basically CurrentValue is the value of the in memory entity (if you change the property CurrentValue changes). OriginalValue is "usually the entity's property values as they were when last queried from the database"
Nevermind ;p