Why EF core tries to add navigational property into DB and not only the Id of foreign model? - asp.net-core

I was wondering why EF tries to add also foreign models.
Example:
public class Category
{
public int Id { get; set; }
public string Name{ get; set; }
}
public class Content
{
public int Id { get; set; }
public string Name{ get; set; }
public Category Category{ get; set; }
}
After creating "Content" using migrations, I have a table that includes the id of category. That's create. So I have three columns: Id, name and the categoryId. Seems EF "knows" that this should be just the primary key of Category, that needs to get stored.
Than I tried to add something with EF.
var cat = new Category {Id = 2, Name = "awesomeCat"})
var addContent = new Content({Name = "test", Category = cat})
Now I want to add a Content by using _context.Add(addContent). I was expecting a single insert into db that uses the name "test" and the categoryId 2. Id will be generated by DB.
But instead EF also tries to add a new Category into the category table.
So I took a deeper look and seems EF "does" not know it already exists and was not maintaining any transactions about the category model.
I gave it another try and used no new category, instead I was loading it before:
var cat = _context.findById("2");
and assigned this one instead. Now EF should know that this one already exists and does not have to add it in category table.
Could it be, that my model is just wrong.
Do I need to use it more like:
public class Content
{
public int Id { get; set; }
public string Name{ get; set; }
public int? CategoryId{ get; set; }
[ForeignKey("CategoryId")]
public Category Category{ get; set; }
}
Won't I get two category references then?

You need to tell EF Core it's a primary key and to generate the key
public class Category
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
}
public class Content
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
Then you don't need to mark [ForeignKey("CategoryId")], EF Core will turn the object reference into an ID in the database
If I misunderstood your question, ask again :)

EF Core has internal tracking of entities. When you simply new up a category, it's not being tracked. When you add the content, EF will track any related entities as well, which would include your category, which will by default be tracked as "Added". You have a few choices.
Don't "new up" an existing category, but rather, retrieve it from the database. If EF pulls it from the database, then it will be tracked, and will not be added again.
You can explicitly track the category instance you newed up and set it's state to "Unchanged".
_context.Attach(category);
_context.Entry(category).State = EntityState.Unchanged;
_context.Add(content);
The best method is to not deal with the reference property at all, and use an explicit foreign key property. Add a property to your content class:
public int CategoryId { get; set; }
Then, you can simply set this id, instead of the Category prop:
var addContent = new Content { Name = "test", CategoryId = 2 };
EF will backfill the reference property after save.

Related

EF Core composite foreign key and constraint

In my project I have noticed that I will be have a lot of dictionaries with the same structure:
shortcut
full name for tooltip
which will be used on many different business forms.
I started to thing that there is no sense to keep all of them in separate tables.
It is better to keep all of them in one table and provide an additional column (DictionaryType) which will separate them in the case of asking the database for data?
So one repository with such method
public class DictionaryEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public string FullName { get; set; }
public DictionaryType Type { get; set; }
}
public async Task<IEnumerable<DictionaryEntity> GetDictionaries(DictionaryType type)
{
return await _dbContext.Dictionaries.Where(d => d.DictionaryType == type).ToArrayAsync();
}
If new dictionaries appear, I need to only extend DictionaryType and I don't need to worry about database changes or repo/service/controller changes.
For now it is nice and easy, but... I would like to configure foreign key in business entities in that way:
public class CarEntity
{
public Guid Id { get; set; }
public Guid ModelTypeId { get; set;}
public DictionaryEntity ModelType { get; set;}
public Guid PetrolTypeId { get; set;}
public DictionaryEntity PetrolType { get; set;}
}
How to configure in EF Core, foreign key in that way where:
CarEntity.ModelTypeId points to DictionaryEntity.Id and DictionaryEntity.Type = DctionaryType.ModelType ?
CarEntity.PetrolTypeId points to DictionaryEntity.Id and DictionaryEntity.Type = DctionaryType.PetrolType ?
I read, that there is something like a composite foreign key, so I could do FK on { dict.Name, dict.Type } but it demands from me to keep in CarEntity as many properties as composite foreign key have.
Is there a chance to do unique constraint across multiple tables ?
Something like this:
modelBuilder.Entity<CarEntity>()
.HasCheckConstraint("CK_ModelType", "[ModelTypeId] IS NOT NULL AND [Document].[Type] = 'ModelType'", c => c.HasName("CK_ModelType_Dictionary"));

Asp.net core Add Soft delete by modelBuilder.Entity<DependenciaDisciplina>().Property<bool>("isDeleted"); but isDeleted as composite key

I insert the soft delete flag with
modelBuilder.Entity<DependenciaDisciplina>().Property<bool>("isDeleted");
But i need to add it as a composite key
Can somebody help me?
I insert the soft delete flag with
modelBuilder.Entity<DependenciaDisciplina>().Property<bool>("isDeleted");
But i need to add it as a composite key
To set the Composite Keys using the Fluent API, we have to use the HasKey() method.
After check the HasKey() method definition, we can see that the parameter should be a string array or an expression.
So, you could use the set the composite key like this (change the Car model to your model):
modelBuilder.Entity<Car>().Property<bool>("isDeleted");
modelBuilder.Entity<Car>().HasKey(new string[] { "CarId", "isDeleted" });
The Car model:
public class Car
{
[Key]
public int CarId { get; set; }
public string CarName { get; set; }
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
Then, after migration, the result like this:

ASP.NET MVC 4 database scaffolding self referential model

I have a constructed a model using code first in C#. The model literally represents a container element for a website building application, in other words, the model defines a Div tag or some such HTML element. Like a Div tag which can contain multiple child elements, I have tried to represent this in my model, but the scaffolding to the DB, does not give me what I'd expect.
I should get a new many to many joins table, but instead I only get a single column in the DB which expects a single int data type.
Here is the model:
public class ElementContainer
{
public int ElementContainerID { get; set; }
public int PageId { get; set; }
public int? ParentElementContainerID { get; set; }
public string ElementContainerName { get; set; }
public ElementType ElementType { get; set; }
public string ElementClass { get; set; }
public PageAsset PageAsset { get; set; } // content of container
public List<ElementContainer> NestedContainers { get; set; }
}
The last line is the self-referential attribute which just appears as a column called ElementContainer_ElementContainerID
Thanks in advance!
I agree with Bahman, DB first is easier.
While I haven't tried to do what you are trying, your code looks like a self-Join that would do exactly what you describe.
This is a One-to-Many relationship. EF Navigation will pull a List of all nested children containers.
If you want to create a many-to-many relationship with EF Code-First, you should create another Entity
public class ContainerChildren
{
public int ElementContainerID { get; set; }
public List<ElementContainer> NestedContainers { get; set; }
}
this reference should help you to get the exact idea http://blogs.msdn.com/b/wriju/archive/2011/05/14/code-first-ef-4-1-building-many-to-many-relationship.aspx

Entity Framework Code First - using UserId key of Memberships/Users table as Foreignkey in a custom table

I am trying to create a SQL table to include additional user information.
I want it to be created by Migrations in VS2012 (package console => update-database).
I see that there are two tables using UserId column as key: Memberships and Users tables.
I am trying to define the following class and map it to SQL table via Migrator:
[Key]
[Column(Order = 0)]
[ForeignKey("User")]
public Guid **UserId** { get; set; }
[Key]
[Column(Order = 1)]
[ForeignKey("Category")]
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
**public virtual User User { get; set; }**
Although it is obvious in the Users SQL table that UserId column is the key, I get this error message:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'User' has no key defined. Define the key for this EntityType.
\tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'Users' is based on type 'User' that has no keys defined.
What I am missing here? Might be that Microsoft.VisualBasic.ApplicationServices.User / System.Web.Security.MembershipUser classes weren't necessarily mapped to the tables this way and vise versa, and therefore the UserId property is not declared is Key dataannotation?
I am open for other solutions for this problem.
Big Thanks!
I am currently using asp.net mvc4 with Azure db.
When I want to use UserId in other tables from UserProfile. Notice in AccountModels there is class UserProfile:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
Now, let's say your category is created by certain user, you can link category entry to user in following way.
[Table("Category")]
public class Category
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int CategoryId { get; set; }
public UserProfile CreatorUserId { get; set; }
}
Otherwise, you can always use UserProfile as model.

PetaPoco to return self-referencing hierarchy

How would one write a query/method to return a POCO that is from a self-referencing database as shown in this question
Firstly you would map it a flat class. eg. db.Fetch<CategoryDb>("select * from categories");
public class CategoryDb {
public int Id { get; set; }
public string Name { get; set; }
public int ParentCategoryId { get; set; }
}
From here I would then create a new Object that self referenced itself. (You could use the existing object with the ParentCategory having the [Result] attribute on it.)
public class Category {
public int Id { get; set; }
public string Name { get; set; }
public Category ParentCategory { get; set; }
}
You could then take this and convert your flat list into a nested list.
I do have code somewhere that can do this, and for which it also provides searching methods etc, but its not on this computer. I will update tomorrow with a link to the code.