EFCore failing to use DbSet naming convention - c#

Using the latest efcore 2 preview from myget, it seems EF under certain conditions is not using the name of the DbSet<> property name as the table name. This is not exactly a simplified example...but this is the gist.
public class Post
{
public int Id { get; set; }
}
public class Context : DbContext
{
public DbSet<Post> Posts { get; set; }
}
In this case, I would expect the table generated to be called Posts... but instead it is using the singular version Post (the name of the entity).
There is no code inside the modelBuilder that is changing the pluralization or anything else that explicitly sets the table name.
Under what conditions would EFCore not default to using the DbSet property name as the table name? Has the default naming convention changed in efcore 2?

Related

EF Core saves null value of required property

I have three classes:
public class Person
{
public string Name { get; set; }
public Guid Guid { get; set; }
}
public class Student : Person
{
public string DOB { get; set; }
}
public class Teacher : Person
{
}
I want to make string DOB as required and I am doing this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().HasKey(d => d.Guid);
modelBuilder.Entity<Student>().Property(d => d.DOB).IsRequired(true);
modelBuilder.Entity<Teacher>();
base.OnModelCreating(modelBuilder);
}
In SQL EF Core is generating nullable column:
And it is allowing to save data when DOB is null:
MainContext mainContext = new MainContext();
mainContext.Add(new Student() { DOB = null });
mainContext.SaveChanges();
However, It is working without inheritance. Is it an EF Core issue or I am missing something in model mapping?
EDIT: I replaced DateTime? to string because the issue is not related to the type of property
Is it an EF Core issue or I am missing something in model mapping?
It is indeed EF Core issue, which also is observed in the EFC 5.0 release candidate, so it won't be addressed (except if you change the database design to utilize TPT).
First, unlike EF6, EF Core does not perform validations in general. Instead, it relies on the underlying database to do that.
Second, since the TPH database inheritance strategy stores all data in a single table, the derived entity data columns must allow null, even though they are required, otherwise you won't be able to store Teacher entity data for instance.
The combination of the two aforementioned behaviors leads to the unexpected behavior of allowing null value for the required field of a derived entity.
Therefore there is nothing you could do at mapping side (neither fluently nor with [Required] data annotation). The necessary validations should be performed by the business logic layer outside the EF Core.
Please don't suggest me to make it DateTime DOB.
I am going to suggest just that, define DOB as:
public DateTime DOB { get; set; }
EF Core uses Table-per-hierarchy here. It is clever enough to figure out that required properties on child types need to be NULL in the database because it needs to be able to store other childs (Teacher) and the base type (Person) where the required property is missing.
I think you are trying to solve a problem that is solved in EF Core itself.
I think your property will be null when no value is present and your Required attribute will behave as expected. try to add Required as an annotation on prop maybe work

Entity Framework Core Database First - Multiple Foreign keys to one table

Background Information
I am currently working with EF Core using a database first implementation.
Current tables
Fizz
{
[Id] INT
[Category] varchar
[Value] varchar
}
Buzz
{
[Id] UniqueIdentifier
[TypeId1] INT
[TypeId2] INT
CONSTRAINT [FK_Buzz_Fizz_1] FOREIGN KEY ([TypeId1] REFERENCES [Fizz][Id])
CONSTRAINT [FK_Buzz_Fizz_2] FOREIGN KEY ([TypeId2] REFERENCES [Fizz][Id])
}
Fizz currently acts a lookup table. Doing this allows for a single data repository to be used to find different values by category.
Buzz is a table that has two different type values to be stored e.g. TypeId1 could be brand which would exist in Fizz as (id, Brands, Nestle) and TypeId2 could be a flavor which would exist in Fizz as (id, Flavors, Grape).
The Issue
I scaffold the db to create the Data Models.
When running the application the following occurrs:
InvalidOperationException: Unable to determine the relationship represented by navigation property 'Buzz.TypeId1' of type 'Fizz'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
One solution that has occurred to me is to break this lookup table (Fizz) into multiple tables that way the references could be resolved by not having duplicate types used for Foreign Keys.
This would require re-work of the logic for the current data repository to either access multiple tables or be split into multiple data repos.
Another solution would be to modify the DBContext that is generated and use DataAnnotations on the DataModel. I would like to avoid doing this as the Context and Models will be regenerated in the future and these changes will be overwritten.
Is there a way to have a datamodel generated from a table that has multiple Foreign Keys to a single table without having to modify the generated code?
For posterity:
With the database approach a scaffold of the db is done to create the context and data models.
The data models generated (using the example tables above) look something like this -
public partial class Buzz
{
public Buzz()
{ }
public Guid Id { get; set; }
public int TypeId1 { get; set; }
public int TypeId2 { get; set; }
public Fizz TypeId1Fizz { get; set; }
public Fizz TypeId2Fizz { get; set; }
}
public partial class Fizz
{
public Fizz()
{ }
public int Id { get; set; }
public string Category { get; set; }
public string Value { get; set; }
public ICollection<Buzz> TypeId1Fizz { get; set; }
public ICollection<Buzz> TypeId2Fizz { get; set; }
}
The issue is that the relationship in Buzz could not be resolved.
The solution
When using scaffold on the database all models are generated as partials to a specified folder. I created a partial for the Buzz class in another directory that lives inside of the directory created by the scaffold (be sure that the namespaces match VS likes to add the directory name to the namespace and the partials won't be matched).
public partial class Buzz
{
[NotMapped]
public Fizz TypeId1Fizz { get; set; }
[NotMapped]
public Fizz TypeId2Fizz { get; set; }
}
but Leustherin then you lose the ability to utilize .Include for Fizz! EntityFramework won't create an SQL join statement for you so you will have to make an extra trip to the DB to obtain your look up value!
To get around this, override the Get or GetAll function of your data repository and create your own join statement.
Why I chose this solution
Maintainability.
Anytime the DataModels are regenerated instead of getting a runtime error there is now a compile error reminding the dev to delete the extra properties from the generated data model.
There is no other modification of automatically generated files.
There are no major schema changes done to accommodate the change.
I will do my best to keep this updated.

EntityFramework 6 DatabaseFirst add navigation property from code

I have (can`t change) EF DataBase first project without navigation property in models.
I want extend autogenerated models and add navigation property
Generated model
//generated.cs
public partial class company
{
public int id { get; set; }
public string name { get; set; }
}
public partial class user
{
public int id { get; set; }
public int company_id { get; set; }
}
I want add navigation property from code
//model_extension.cs
public partial class user
{
public company Company { get; set; }
}
I have exception "The specified type member 'Company' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."
I work with CodeFirst before.
I understand, I must link user.company_id to Company
But not understand how make this with code (not designer)
In Database First Approach, You are generating your POCO objects from database schema via Entity Framework Designer/ADO.NET Entity Data Model so it is not flexible as Code-First, you need to go on database, and change the schema yourself and update your .edmx file. while adding properties to these Models are possible in c# side, but they are not going to be added to your database schema,
I suggest your reverse your database schema and go as Code-First Approach, This nuget package can do this for you.
After Reversing It's all about Code-First then, creating your own DbContext and OnModelCreating and let the Migration handle the rest. Then you can use Eager Loading of EF to load your data,

Discriminator on Base Class on Code First

I have two nearly identical tables, Person and PersonArchive. As you can imagine PersonArchive is incredibly similar to Person, it just has the addition of two extra fields. I created the class structure below, so if an additional column gets added to Person it automatically appears in PersonArchive:
public partial class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
public partial class PersonArchive : Person
{
public string ArchivedBy { get; set; }
public DateTime ArchivedAt
{
get
{
return DateTime.Now;
}
}
}
My solution reads the data out of Person, fills in the the extra two fields and writes it to PersonArchive. The problem is when I try reading the data out of Person I get the missing discriminator issue, whilst looking at various posts on SO, adding NotMapped to the PersonArchive resolves the issue in reading from Person but obviously it causes problems when inserting to PersonArchive.
I've checked various posts, but non of them seem to match my issue - or at least to my understanding:
Code First: Avoid discriminator column and keep inheritance
https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-inheritance-with-the-entity-framework-in-an-asp-net-mvc-application
Entity framework code first creates "discriminator" column
Entity Framework: How to avoid Discriminator column from table?
You should define another inheritance strategy. TPT - Table Per Type would be appropriate in your case. With this approach you won't event have Disciriminator column.
Table Per Hierarchy is the default inheritance strategy in Entity Framework. Also bear in mind, that TPT inheritance strategy is not yet available in ASP.NET Core

Include statement not working with LINQ query

Trying to iron out some issues with a an MVC project using models derived from existing database tables the developer built, but none of those tables has its relationships set up (they all have primary keys, just no relationships). As such the project uses ViewModels to get some things done when more than one table is needed.
I'm attempting to get around this by adding the necessary items to relate two of the tables.
Table One's POCO:
namespace Project.Models
{
using System;
using System.ComponentModel.DataAnnotations;
public partial class Table_One
{
[ScaffoldColumn(false)]
public short ID { get; set; }
[Display(Name="Name")]
public string NAME { get; set; }
[Display(Name = "Owner")]
public string OWNER { get; set; }
[Display(Name = "Property")]
public Nullable<decimal> PROPERTY { get; set; }
public virtual Table_Two Table_Two { get; set; }
}
}
Table Two's POCO:
namespace Project.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class Table_Two
{
public Table_Two()
{
this.Table_One = new HashSet<Table_One>();
}
[ScaffoldColumn(false)]
public short ID { get; set; }
[Display(Name="Property")]
[MaxLength(20)]
public string Property { get; set; }
public ICollection<Table_One> Table_One { get; set; }
}
}
Were these tables set up with their relationships, they'd be joined on the Property values (I've sanitized the table names and properties). As far as I can tell I've set things up to mirror the way another project I've been working on, which has tables that are related, has been set up to include the relationships, but when I run this LINQ query:
var model = context.Table_One.Include(t => t.Table_Two);
I get the following error message:
"A specified Include path is not valid. The EntityType 'X.Table_One'
does not declare a navigation property with the name 'Table_Two'."
Originally these were being joined via a LINQ query using query syntax to select each of the table's properties into the ViewModel's properties.
I've tried removing the include, but that doesn't gel with other code in the controller. I've tried changing the ICollection to an IList to no avail. I've searched other answers here but none of them seem to solve the problem I'm having.
As far as I know the developer who started the project built the POCOs using a code generation tool that was run against the EDMX in the project. The only other thing I can think of at this point would be to have the developer add the relationships, then update the POCOs to pull in the updated tables via the EDMX.
I should also specify that the ID in Table_Two would be a foreign key in the PROPERTY column of Table_One.
You should use Include with properties which are collections of related entities
var model = context.Table_Two.Include(t => t.Table_One);
Include tries to fetch collection of related entities by using joins on sql query.
If you want to load a related entity you can use lazy loading or load it using the code below
context.Entry(table_one).Reference(x => x.Table_Two).Load();
To rely on Entity Framework's related entities loading, you need to set the entities relations.
Since you said "but none of those tables has its relationships set up", you won't be able to use nor lazy neither eager loading, you have to fetch the other entities by hand.

Categories

Resources