I have an entity named PageItem. PageItem has a property named Page. Type of Page is Page class.
class PageItem {
public Page Page { get; set; }
...
}
when I query like this:
var item = context.PageItems.Find(5);
Problem is, item.Page is null, so when I save item entity framework creates a new page record.
Summary of what I learned:
First: entity framework Find method does not fill Id value of nested objects (in database language: foreign keys. But I realized than when you save that entity, it does not update foreign key columns, so nothing is broken.
Seconly: if you want to read, and use foregin key values of an entity, you should define (int/long whatever) properties per referanced table an foreign key. And mark it with ForeignKey attribute. Also if it can be null, make property type int? or long? (nullable)
Sample:
pubclic class Customer {
public Id { get; set; }
[ForeignKey("City")}
public int? City_Id { get; set; }
public City City { get; set; }
}
Also if you want layz loading on City, you have to mark it virtual.
Related
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.
Using Entity Framework Code First, I'm seeing very strange behavior when inserting a row with a composite key. The composite key consists of a guid ID field and a guid foreign key field, creating an "identifying relationship". The strange behavior is that regardless of what I set the ID and foreign key field to, the generated SQL sets them both to the foreign key value.
My classes look like this:
public class Parent {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public Guid ParentId { get; set; }
}
In my DbContext file I have:
modelBuilder.Entity<Child>().HasKey(c => new { c.Id, c.ParentId });
Doing something like:
var parent = new Parent() { Id = Guid.NewGuid() };
var child = new Child() { Id = Guid.NewGuid(), ParentId = parent.Id };
parent.Children.Add(child);
You'd think the SQL executed would insert a new child with differing Id and ParentId values. But instead, what I'm seeing is:
// Assume parent is already in the DB, with ID of '1b1a6ecd-00ad-4265-ac0d-9a50bd30e247'
INSERT [dbo].[Child]
([Id],
[ParentId])
VALUES ('1b1a6ecd-00ad-4265-ac0d-9a50bd30e247' /* #0 */,
'1b1a6ecd-00ad-4265-ac0d-9a50bd30e247' /* #1 */)
Why is the SQL using the ParentId value for both fields? This doesn't make sense at all.
UPDATE
Unless I totally misunderstand something fundamental to EF, I think this must be a bug. I've uploaded a tiny reproducible project to http://1drv.ms/1kX2oVC
It uses EF 6.1 and .NET 4.5. I'm hoping some EF expert can chime in here and confirm this is a bug, or that I'm doing something fundamentally wrong.
With this set up Entity Framework isn't able to properly infer the associations. You have two options how to fix it:
Add modelBuilder.Entity<Parent>().HasMany(x => x.Children).WithRequired().HasForeignKey(x => x.ParentId); into your OnModelCreating.
or
Add public Parent Parent { get; set; } into your Child entity.
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 have some urgent issue which I could not find answer for across the web.
I am using CodeFirst EF 4.3.1 and I am getting an error:
Violation of PRIMARY KEY constraint 'PK_T_CRProviders'. Cannot insert duplicate key in object 'dbo.T_CRProviders'.
My code is:
Models:
public enum CRProviderEnums
{
PE_Abcd = 0,
PE_Efgh
}
[Table("T_CRProviders")]
public class CRProvider
{
[Key]
[Required]
public int Enum { get; set; }
[Required]
public string Name { get; set; }
}
[Table("T_CRSupportedResources")]
public class CRSupportedResource
{
[Key]
public Guid SupportedResourceId { get; set; }
[Required]
public CRProvider Provider { get; set; }
}
DbContext:
public class RSContext : DbContext
{
public DbSet<CRProvider> CRProviders { get; set; }
public DbSet<CRSupportedResource> CRSupportedResources { get; set; }
}
Table T_CRProviders looks like this: Enum (PK), Name
Table T_CRSupportedResources looks like this: SupportedResourceId (PK), Provider_Enum (FK).
In the database table T_CRProviders I already have a provider with the following values:
Enum: 0 (which is PE_Abcd)
Name: "PE_Abcd"
Now my main() calls a method AddSupportedResource. This method adds to table T_CRSupportedResources a new CRSupportedResource which refers to provider 0 (PE_Abcd). The method looks like this:
public void AddSupportedResource()
{
CRSupportedResource supportedResource = new CRSupportedResource()
{
SupportedResourceId = Guid.NewGuid(),
Provider = new CRProvider()
{
Enum = (int)CRProviderEnums.PE_Abcd,
Name = "PE_Abcd"
}
};
using (RSContext myContext = new RSContext())
{
myContext.CRSupportedResources.Add(supportedResource);
myContext.SaveChanges();
}
}
I expect that this method will leave table T_CRProviders untouched, and add a new row to table T_CRSupportedResources which will look like this:
SupportedResourceId: DE532083-68CF-484A-8D2B-606BC238AB61
Provider_Enum (FK): 0 (which is PE_Abcd).
Instead, upon SaveChanges, Entity framework also tries to add Provider to the T_CRProviders table, and since such a provider already exists it throws the following exception:
An error occurred while updating the entries.
Violation of PRIMARY KEY constraint 'PK_T_CRProviders'. Cannot insert duplicate key in object 'dbo.T_CRProviders'.
The statement has been terminated.
My question:
How can I instruct the EF not to update table T_CRProviders upon updating table T_CRSupportedResources?
Btw, in the SQL Server I see that table T_CRSupportedResources has a foreign key named FK_RW_TCRSupportedCloudResources_RW_TCRCloudProviders_Provider_Enum and its Update Rule has the value of No Action.
I expect that this method will leave table T_CRProviders untouched,
and add a new row to table T_CRSupportedResources
No it will not happen. You are creating detached entity graph consisting of existing entity a and new entity. EF doesn't know about the existence of your entity until you inform it about it - there are no DB queries validating existence performed by EF on behind.
If you call Add method all entities in your entity graph are added as new. If you don't want to insert all of them you can start with using Attach and manually change state for new ones. For example like:
myContext.CRSupportedResources.Attach(supportedResource);
myContext.Entry(supportedResource).State = EntityState.Added;
Actually, there is a way to do this.
See the answer to my question in the following link:
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/62f3e5bc-c972-4622-b830-e7d7fe710101
Here is the simple case.
I have a Customer record defined like this:
public class Customer
{
[Key]
public string Id { get;}
public UsState UsState { get; set; }
}
public class UsState
{
[Key]
public string StateAbbreviation {get;set;}
public string StateName {get;set;}
}
I already have my UsState's populated with the 50 states, now I want to insert one Customer record that ties to an existing state.
If I say something like:
Customer customer = new Customer()
{
UsState = "CA",
Id = 1001
}
dbContext.Customers.add(customer);
I get an error saying state is trying to be inserted again.
How can I get the add to use an existing state and not try and re-insert it. My real problem has lots of nested objects that may exist so I need to solve this in general.
Thanks
Two ways I know is:
Set EntityState to what is appropriate for your action or
Get the UsState from the database and add it to the customer right before saving, which will give it an EntityState of UnModified or something like that.
to change the EntityState you need to get to the ObjectContext of your DbContext like shown in this thread: Entity Framework Code First - No Detach() method on DbContext
If you think about this in terms of table data, your Customer table should have a column in it that describes the customer's state. You also have a state table that you can use to join together to get the state name.
CREATE TABLE customer (
id VARCHAR(10),
state CHAR(2)
)
CREATE TABLE state (
stateabbreviation CHAR(2),
statename VARCHAR(50)
)
The "state" column is a just a string representing the state. So your class should be defined the same way, with and ID and a State, and if you want to include the information for your USState class, defined a property of type UsState with a ForeignKey defined:
public class Customer
{
[Key]
public string Id { get;}
public string State { get; set; }
[ForeignKey("State")]
public UsState UsState { get; set; }
}
Then, when you create a new record, set the text of the string, not the UsState object.
Customer customer = new Customer()
{
State = "CA",
Id = 1001
}
dbContext.Customers.add(customer);