I have an existing database, to which I'd like to add Entity Framework mappings for just a handful of tables/entities. Is there a way to ignore all entities by default, and then selectively include them?
I have this in the context constructor to not migrate changes:
Database.SetInitializer(new NullDatabaseInitializer<Context>());
And then I have the following fluent code to map the existing entities:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Cube>()
.Map(e => e.ToTable("tblCubes"))
.HasKey(e => e.CubeId);
...
However, when I run any EF queries, I get the error:
One or more validation errors were detected during model generation.
EntityType 'xyz' has no key defined. Define the key for this EntityType
Rather than using modelBuilder.Ignore<xyz>(); on every existing and future entity, can't I just get EF to ignore all by default, and only map those I choose/include?
EDIT============
One of my EF entities (CubeFact) has relational properties to other classes like this one below to the Year class:
private Year _year;
public Int16 YearId { get; set; }
public Year Year { get { return _year ?? (_year = Year.GetYearById(YearId)); } set { _year = value; } }
The Year class then links to a Fact class, which is one of the classes failing validation. But neither the Year class nor the Fact class have been explicitly mapped. Does EF follow these relationships and then validate, even if I haven't explicitly told it about the relationships?
Related
I have my entity defined like this:
public class Entity : BaseModel // Has the already ID defined
{
private int? companyId;
public Company? Company { get; set; }
public int? CompanyId {
get => this.companyId == 0 ? null : this.companyId; // I tried this for debugging purposes to force this value to "null" -> made no difference
set => this.companyId = value;
}
}
public class Company : BaseModel // Has the already ID defined
{
public IEnumerable<Entity> Entities { get; set; } = new List<Entity>();
}
Anyway, if I set the CompanyId to null, my DB throws an exception with the message: "FOREIGN KEY constraint failed". If the CompanyId is set to, e.g. 123, the relationship is resolved accordingly.
I mean, it makes sense, that EF cannot find null in my DB, but how do I want to set an optional value otherwise? I am using code first annotations only, hence my OnModelCreating of my context is completely empty.
How are you loading the entities in the first place? Are you loading an Entity by ID and trying to dis-associate it from a company, or have you loaded a company with it's entities and trying to remove one association?
Normally when working with relations where you have navigation properties, you want to de-associate them (or delete them) via the navigation properties, not the FK properties. For instance if loading a company and wanting to de-associate one of the entities you should eager-load the entities then remove the desired one from the collection:
var company = _context.Companies.Include(c => c.Entitites).Single(c => c.Id == companyId);
var entityToRemove = company.Entities.SingleOrDefault(e => e.Id == entityId);
if(entityToRemove != null)
company.Entities.Remove(entityToRemove);
_context.SaveChanges();
Provided that the relationship between Company and Entity is set up properly as an optional HasMany then provided these proxies are loaded, EF should work out to set the entityToRemove's FK to null.
If you want to do it from the Entity side:
var entityToRemove = _context.Entities.Include(e => e.Company).Single(e => e.Id == entityId);
entityToRemove.Company = null;
_context.SaveChanges();
That too should de-associate the entities. If these don't work then it's possible that your mapping is set up for a required relationship, though I am pulling this from memory so I might need to fire up an example to verify. :) You also should be checking for any code that might set that CompanyId to 0 when attempting to remove one, whether that might be happening due to some mapping or deserialization. Weird behaviour like that can occur when entities are passed around in a detached state or deserialized into controller methods. (which should be avoided)
Update: Code like this can be very dangerous and lead to unexpected problems like what you are encountering:
public virtual async Task<bool> Update(TModel entity)
{
Context.Update(entity);
await Context.SaveChangesAsync();
return true;
}
Update() is typically used for detached entities, and it will automatically treat all values in the entity as Modified. If model was already an entity tracked by the Context (and the context is set up for change tracking) then it is pretty much unnecessary. However, something in the calling chain or wherever has constructed the model (i.e. Entity) has set the nullable FK to 0 instead of #null. This could have been deserialized from a Form etc. in a view and sent to a Controller as an integer value based on a default for a removed selection. Ideally entity classes should not be used for this form of data transfer from view to controller or the like, instead using a POCO view model or DTO. To correct the behaviour as your code currently is, you could try the following:
public async Task<bool> UpdateEntity(Entity entity)
{
var dbEntity = Context.Set<Entity>().Include(x => x.Customer).Single(x => x.Id == entityId);
if (!Object.ReferenceEquals(entity, dbEntity))
{ // entity is a detached representation so copy values across to dbEntity.
// TODO: copy values from entity to dbEntity
if(!entity.CustomerId.HasValue || entity.CustomerId.Value == 0)
dbEntity.Customer = null;
}
await Context.SaveChangesAsync();
return true;
}
In this case we load the entity from the DbContext. If this method was called with an entity tracked by the DbContext, the dbEntity would be the same reference as entity. In this case with change tracking the Customer/CustomerId reference should have been removed. We don't need to set entity state or call Update. SaveChanges should persist the change. If instead the entity was a detached copy deserialized, (likely the case based on that 0 value) the reference would be different. In this case, the allowed values in the modified entity should be copied across to dbEntity, then we can inspect the CustomerId in that detached entity for #null or 0, and if so, remove the Customer reference from dbEntity before saving.
The caveats here are:
This won't work as a pure Generic implementation. To update an "Entity" class we need knowledge of these relationships like Customer so this data service, repository, or what-have-you implementation needs to be concrete and non-generic. It can extend a Generic base class for common functionality but we cannot rely on a purely Generic solution. (Generic methods work where implementation is identical across supported classes.)
This also means removing that attempt at trying to handle Zero in the Entity class. It should just be:
public class Entity : BaseModel
{
public Company? Company { get; set; }
[ForeignKey("Company")]
public int? CompanyId { get; set; }
// ...
}
Marking Foreign Keys explicitly is a good practice to avoid surprises when you eventually find yourself needing to break conventions that EF accommodates in simple scenarios.
In Core 3.0 i could join multiple dbs / schemas.
Here is the class Order in dbEarth :
namespace dgNet.Core.Models.Earth
{
[Table("tbl_Order", Schema ="Earth")]
public class Order : EntityBaseWithTypedId<int>
{
[Key]
[Column("BestID")]
public override int Id { get; set; }
Here is class SerialNumber in dbMars
namespace dgNet.Core.Models.Mars
{
[Table("tbl_serialnumber", Schema = "Mars")]
public class SerialNumber : EntityBaseWithTypedId<int>
{
[Column("serialnumber")]
public int Serialnumber { get; set; }
[Column("jobId")]
public int JobId { get; set; }
[ForeignKey("JobId")]
public Order Order { get; set; }
Data Annotations is equivalent to the code here :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().ToTable("tbl_Order", "Earth");
}
So if builded a LINQ Query and included Order(dbEarth) in SerialNumber(dbMars) it worked well.
query => query.Include(serialNumber => serialNumber.Order).FirstOrDefault();
After the update to Core 5.0 SQL- Querys are created incorrectly.
SQL joins Orders on the same db / schema like SerialNumbers.
Using newest NuGet packages .AspCore (5.0.10)
DB = MySQL
Using Pomelo.EntityFrameworkCore.MySql (5.0.2)
Someone has that problem?
MySQL does not support the EF Core concept of schemas.
The EF Core concept of schemas is the same one that SQL Server uses, in which schemas are basically just categories (organization units) that you can use to group multiple tables logically together within the same database.
What MySQL calls schemas are actually databases, and a single DbContext does not support multiple databases in EF Core.
Therefore, we officially removed the very brittle multi-database support in Pomelo 3.2.0.
The official way to deal with this is shown in Implement alternatives to the current behavior to always throw, if a schema has been set for an object #982:
There are currently 3 options to choose from:
// Throw an exception, if a schema is being used. This is the default.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Throw))
// Silently ignore any schema definitions.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Ignore))
// Use the specified translator delegate to translate from an input schema and object name to
// an output object name whenever a schema is being used.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Translate,
(schema, entity) => $"{schema ?? "dbo"}_{entity}"))
There is also a way to explicitly enable the old behavior, as illustrated in
method ModelBuilder.HasDefaultSchema is not working (No database selected)
#22971 (comment) for Pomelo 3.2.x:
[...]
In essence, there are two steps:
You need to derive from MySqlSqlGenerationHelper and override GetSchemaName:
public class CustomMySqlSqlGenerationHelper : MySqlSqlGenerationHelper
{
public CustomMySqlSqlGenerationHelper(
RelationalSqlGenerationHelperDependencies dependencies,
IMySqlOptions options)
: base(dependencies, options)
{
}
protected override string GetSchemaName(string name, string schema)
=> schema; // <-- this is the first part that is needed to map schemas to databases
}
You need to provide a schema name translator:
optionsBuilder
.UseInternalServiceProvider(serviceProvider) // use our ServiceProvider
.UseMySql(
"server=127.0.0.1;port=3308;user=root;password=;database=EFCoreIssue22971_01_IceCreamParlor",
b => b.ServerVersion("8.0.21-mysql")
.SchemaBehavior(
MySqlSchemaBehavior.Translate,
(schemaName, objectName) => objectName) // <-- this is the second part that is needed to map
// schemas to databases
.CharSetBehavior(CharSetBehavior.NeverAppend))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
I'm currently using EF5 in a project with a legacy database. The legacy application uses dynamically build tables (xxxx_year, yyyy_year) to store "year based data". I've been trying to find a way to dynamically map the ef entities (xxxx, yyyy, etc) to the tables, based on the year property value, but I always end up getting the "The model backing the context has changed since the database was created." error. Can anyone give me some ideas on how to accomplish this ?
I found some old blog posts talking about edm mapping, where we can separate mapping tables based on some property value (kind of horizontal partitioning), but I can't find any pointers on how to accomplish the same using code first.
Thanks, P
In your mapping configuration for each domain object, you can tell EF that the corresponding table name for an entity is different from the entity name itself.
If your class is called YyyyYear, it can point to a table called "2012_year" by specifying the name in its mapping file.
e.g.
// 1 entity class per db table
public class YyyyYear
{
public int Id { get; set; }
}
// 1 mapping file for entity
using System.Data.Entity.ModelConfiguration;
public class YyyyYearMap: EntityTypeConfiguration
{
public YyyyYearMap()
{
this.HasKey(t => t.Id);
this.ToTable("2012_year");
}
}
// your db context class (derives from DbContext)
using System.Data.Entity;
public class MyDbContext: DbContext
{
// 1 db set for every entity/table
public DbSet YyyyYears { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// 1 mapping file for every entity/table
modelBuilder.Configurations.Add(new YyyyYearMap());
}
}
I'm not sure if that's what you're looking for, but I have a blog post with step-by-step instructions, a working sample, and how to resolve common issues.
http://wakeupandcode.com/entity-framework-code-first-migrations/
Hope this helps!
I'm using the newest Entity Framework and ran into a problem with Many To Many Relationship when I want to create an extra column.
The issue is the same raised in this older post:
EF Code First Additional column in join table for ordering purposes
Is it still the problem today that one can not add an extra column without loosing the many to many relation ship (link from object A to B as A.B because the mapping becomes and entity it self) ?
What are the work a rounds ?
Look up the a of class A I need and then query for mapping table where(e=>e.A == a) to get my Bs? And when I need the extra colums i would do MappingTable.find(a,b) ?
Are there other modeling options, linq to sql that would make it easier ?
As far as I know things haven't changed with EF 5. You would need to do it as the link says to. I like to stick with EF as its easy to use, but that's just my opinion...
I had the same problem. What I did to work-around it was create another derivative DbContext specifically to handle joins. I.E.:
public class JoinContext : DbContext
{
internal JoinContext() : base("name=SampleConnectionString")
{
PreventErrorIfDatabaseSchemaChanges();
// Get the ObjectContext related to this DbContext
var objectContext = (this as IObjectContextAdapter).ObjectContext;
}
public DbSet<StudentImage> StudentImages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentImage>()
.ToTable("StudentImages");
.HasKey(joinTable => new { joinTable.StudentId, joinTable.ImageId });
base.OnModelCreating(modelBuilder);
}
private static void PreventErrorIfDatabaseSchemaChanges()
{
Database.SetInitializer<JoinContext>(null);
}
}
I left the other application context with the Student/Image many-to-many join mapping as-is. Don't forget to specify a compounded key for the join table (refer to HasKey method above), else EF bombs on databse initialization.
After you have your special join context, use a repository to access this context and get or set the desired fields from mapped join table:
public class StudentRepository
{
public int GetImageSortOrder(int studentId, int imageId)
{
var joinContext = new JoinContext();
var joinTuple = joinContext.StudentImages.Find(studentId, imageId);
return joinTuple.SortOrder;
}
}
Hope this helps!
I just want to know if there's a way on how to create an Updatable model. Right now, I have to create procedures for insert, update, and delete for all of the tables in my model. This is very tedious so I was wondering if there is one way which I could do to resolve this?
I remember before in my previous work that we used to make models and access them (CRUD) without creating procedures. But i'm not really certain now on how it was made.
Thank you!
There are various ways in which you can automate the generation (on the fly or already generated at compile time) of the actual SQL calls to the database to insert, select, update and delete within the Entity Framework.
You can use the ORM tools (e.g. Linq to Entities) to minimise or eliminate the writing of raw SQL. This means you still have to use the correct attributes on your entities and the properties/methods therein and that's a manual process. (Some backgrounding on this MSDN page)
You can allow the framework to automatically generate your entities based on some existing database schema (only possible with SqlServer-type databases) which basically does 90% of the work for you. There may be some cases where you need to override, for example, the default insert SQL with something custom. This is achieved via the Generate Database Wizard (which I think is a part of Visual Studio 2008+).
You can use POCO classes with EF. If you're using 4.1 and above, you can use the DbContext class. To map your model to the table / columns, simply override OnModelCreating in your context class (which inherits from DbContext). Say you have a model called User, a table called Users, and the context class MyContext, the code could be smth like this:
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
}
public class MyContext : DbContext
{
public MyContext() :
base("MyContext")
{
}
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().
.ToTable("Users");
modelBuilder.Entity<User>().
.Property(d => d.UserId)
.HasColumnName("UserId")
modelBuilder.Entity<User>().
.Property(d => d.UserName)
.HasColumnName("UserName");
}
}
To use it, simply add the User instance to your DbSet, then call SaveChanges:
using(MyContext ctx = new MyContext())
{
var u = new User() { UserId = 1, UserName = "A" };
ctx.Users.Add(u);
ctx.SaveChanges();
}