EF keep existing data during automatic migration - c#

I am having a strange issue where my EF Automatic Migrations are removing all of the data within from all tables whenever I make a change to any one of the Entity's.
I've seen this similar question however for the life of me I can't derive from the links what I am missing in my own implementation.
Note that the application works as completely fine, with data being retrieved and saved to the mysql database via EF - it's just a great annoyance to have to re-create all the data everytime I want modify an Entity.
I am using EF6.0 and a MySql database.
Context.cs
namespace Dock.Models
{
[DbConfigurationType(typeof(MySql.Data.Entity.MySqlEFConfiguration))]
public class DockContext : DbContext
{
public DockContext(): base("name=DockContext")
{
Database.SetInitializer(new DockInitializer());
}
public virtual DbSet<Profile> Profiles { get; set; }
public virtual DbSet<Dock> Docks { get; set; }
public virtual DbSet<Crate> Crates { get; set; }
public virtual DbSet<Subscription> Subscriptions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
}
Confirguration.cs
internal sealed class Configuration : DbMigrationsConfiguration<DockContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
SetSqlGenerator("MySql.Data.MySqlClient", new MySqlMigrationSqlGenerator());
CodeGenerator = new MySqlMigrationCodeGenerator();
}
protected override void Seed(DockContext context)
{
Profile admin = new Profile {
NotAPrimaryId = 168879070,
DisplayName = "Admin101"
};
context.Profiles.AddOrUpdate(a => a.NotAPrimaryId , admin);
context.SaveChanges();
}
}
And an example of one of the Entity's Dock.cs
namespace Dock.Entities
{
public class Dock
{
[Key]
public int DockId { get; set; }
public int ProfileId { get; set; }
public int GameId { get; set; }
public string GameData { get; set; }
public bool Active { get; set; }
// Navigational Properties
[ForeignKey("CrateId")]
public virtual List<Crate> Crates { get; set; } = new List<Crate>();
}
}

EF will create migration classes for you. You don't have to write them by hand.
Automatic migrations can cause problems in my experience when different team members commit updates at different times.
I've also had to hand-edit the generated code-based migrations on several occasions, something that can't be done with automatic migrations.
I suggest switching to manual migrations so that you have complete visibility into and control of the migration process.

Related

.Net Core API with EF Core Code First and IdentityUser

I have an existing application that I am re-writing as .NET Core API with a ReactJS front-end. I am still in the API end, and I've run into a problem.
CODE
I have a BbUser.cs entity class with the following code:
public class BbUser : IdentityUser
{
public int Points { get; set; } = 0;
public string DisplayUsername { get; set; }
}
And I also have an Artist.cs entity class:
public class Artist
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
[MaxLength(100)]
public string UrlFriendly { get; set; }
public string ImageUrl { get; set; }
public bool IsVerified { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Lyric> Lyrics { get; set; } = new List<Lyric>();
public string UserId { get; set; }
public BbUser User { get; set; }
}
I need a one-to-many relationship between BbUser and Artist. One user can submit many artists and lyrics ...etc. Simple stuff really.
PROBLEM
The application builds fine, but when I attempt to run it by hitting a controller that requires access to the Database, I get the following error:
The entity type 'IdentityUserLogin' requires a primary key to be defined.
I had this issues with regular EF Code First (not Core) and the fix for that, does not work here.
This model worked for me(compiled, and no exceptions at runtime) if I used next code in the DbContext class:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<BbUser>(b => b.ToTable("AspNetUsers"));
base.OnModelCreating(builder);
}
Without calling base.OnModelCreating(builder) I get the same error, because in this case context isn't applying the Identity related schema.
UPDATE:
Everything works fine for me as you can see from the screenshot below:
I have one more idea why you can have such an error. Did your BbContext inherit from DbContext class or IdentityDbContext<IdentityUser>? Because I got the same error that was on your screenshot if I used usual DbContext class.
In order to Idenity tables work fine you should use IdentityDbContext<IdentityUser>. Below the whole code for my working DbContext class
public class BbContext :IdentityDbContext<IdentityUser>
{
public BbContext(DbContextOptions options):base(options)
{
Database.EnsureCreated();
}
public DbSet<Artist> Artists { get; set; }
public DbSet<Lyric> Lyrics { get; set; }
public DbSet<Heart> Hearts { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<BbUser>(b => b.ToTable("AspNetUsers"));
builder.Entity<Heart>().HasKey(h => new {h.UserId, h.LyricId});
base.OnModelCreating(builder);
}
}
Please do try declaring a Guid property named Id, with both Get and Set on the IdentityUserLogin entity.
Another option is to declare a property and decorate it with [Key] attribute

Entity Framework - inheriting from model

I'm new to the Entity Framework and I've followed tutorials online to create my SQL Server database and made several models and a context class to include properties to access them.
This is my account model:
public class Account
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
This is my context class:
public class DashContext : DbContext
{
public DashContext()
: base(Constants.ConnectionString)
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<DashContext>(null);
}
public DbSet<Account> Accounts { get; set; }
}
This works - when I access the DbSet property I can access all the account entires in my database.
However, I want to create an implmentation of the Account class that contains more properties than just columns because it has to interact with my program.
So, I tried to do the following:
public class GameAccount : Account
{
public int SomeSpecialProperty { get; set; }
}
However, when I'm using my context class to get the Account object, I'm not sure how to convert it to GameAccount. I know I can create a constructor that copies the properties from Account to GameAccount, like this:
public class GameAccount
{
public int ID { get; private set; }
public string Username { get; private set; }
public string Password { get; private set; }
public GameAccount(Account model)
{
this.ID = model.ID;
this.Username = model.Username;
this.Password = model.Password;
}
}
...but that seems a bit inefficent to me and I'm sure there's a simpler way.
What do you think?
You have a few options:
Option 1
Use a partial class as indicated by Fruchtzwerg.
Option 2
You can use AutoMapper to map the items from one type to the other. Here is an example:
// Notice the ReverseMap call. That will allow mapping in both directions.
Mapper.Initialize(cfg =>
cfg.CreateMap<Account, GameAccount>().ReverseMap());
var account = Mapper.Map<Account>(new GameAccount());
var gameAccount = Mapper.Map<GameAccount>(account);
Copy Constructors could be very costly to develop and maintenance. Typically generated classes of the Entity Framework are partial.
BradleyDotNET explains:
When code is generated; you don't want your additional methods/properties/whatever blown away, so the designers mark such classes partial to allow users to put additional code in a different file.
So a possible approach is extending the class
public partial class Account
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
with additional properties like
public partial class Account
{
public int SomeSpecialProperty { get; set; }
}

Cannot create more than one clustered index on table

I am having the following error after typing update-database:
Cannot create more than one clustered index on table 'dbo.AppUsers'. Drop the existing clustered index 'PK_dbo.AppUsers' before creating another.
I am working on an Azure mobile service.
I have three data models:
public class AppUser : EntityData
{
public string Username { get; set; }
public virtual ICollection<RatingItem> userRatings { get; set; }
}
public class PlaceItem : EntityData
{
public string PlaceName { get; set; }
public int Group { get; set; }
public string XCoordinate { get; set; }
public string YCoordinate { get; set; }
}
public class RatingItem : EntityData
{
public int Ratings { get; set; }
public string PlaceId { get; set; }
public AppUser user { get; set; }
}
It has to do with migration because :
The initial create is in the _MigrationHistory table, but isn't in the migration folder in the solution explorer.
When I add-migration AddAll, I don't get any errors, and AddAll appears in the migration folder, but not in the table.
In the context file:
public class ICbackendContext : DbContext
{
public DbSet<AppUser> AppUsers { get; set; }
public DbSet<PlaceItem> PlaceItems { get; set; }
public DbSet<RatingItem> RatingItems { get; set; }
}
Generally, this error message is caused by not running the Mobile Apps/Mobile Services DB generator. Entity Framework does not have an annotation for creating a clustered index that is not a primary key, so the mobile server SDK manually creates the right SQL statements to set CreatedAt as a non-primary key clustered index.
To resolve this, run the database generator before migrations are applied. In the Migrations\Configuration.cs file, include the following:
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetSqlGenerator("System.Data.SqlClient", new EntityTableSqlGenerator());
}
To learn more, see How to make data model changes to a .NET backend mobile service. The topic applies to both Mobile Services and Mobile Apps, though some namespaces are different in Mobile Apps.
As stated by #gorillapower in comments, this piece of code is also very important.
modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>( "ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
inside your
protected override void OnModelCreating(DbModelBuilder modelBuilder)
in the DbContext config class. Do not forget to regenerate migrations.

Entity Framework Table per Hierarchy not creating Discriminator

I created an inheritance hierarchy after a few migrations. Now when I update the database using code first migrations, code-first is not automatically creating the discriminator field. I have since dropped the table and recreated it (using code-first migrations) without any luck. The only thing I can think of is that there are no additional "non-virtual" properties in the derived classes--the inheritance structure was created to enforce a business rule that only a certain derived type can have a relationship with another entity.
Base Type:
public abstract class Process
{
private ICollection<ProcessSpecification> _specifications { get; set; }
protected Process()
{
_specifications = new List<ProcessSpecification>();
}
public Int32 Id { get; set; }
public String Description { get; set; }
public Int32 ToolId { get; set; }
public virtual Tool Tool { get; set; }
public virtual ICollection<ProcessSpecification> Specifications
{
get { return _specifications; }
set { _specifications = value; }
}
}
Derived class (no different/unique scalar properties):
public class AssemblyProcess : Process
{
private ICollection<AssemblyProcessComponent> _components;
public AssemblyProcess()
{
_components = new List<AssemblyProcessComponent>();
}
public virtual ICollection<AssemblyProcessComponent> Components
{
get { return _components; }
set { _components = value; }
}
}
Another derived type
public class MachiningProcess : Process
{
private ICollection<MachiningProcessFeature> _features;
public MachiningProcess()
{
_features = new List<MachiningProcessFeature>();
}
public virtual ICollection<MachiningProcessFeature> Features { get { return _features; } set { _features = value; } }
}
Is code-first not adding the discriminator column in the database because it doesn't see any differences between the derived classes (because of there not being any unique "non-virtual" properties)? If so, how do I get around this? If not, what are some reasons why code-first would not automatically create the discriminator column in the database? I have another TPH structure that works exactly the way it's supposed to.
DbContext:
public LineProcessPlanningContext()
: base("LineProcessPlanning")
{
}
public DbSet<Component> Components { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Feature> Features { get; set; }
public DbSet<OperationDefinition> OperationDefinitions { get; set; }
public DbSet<PartDesign> PartDesigns { get; set; }
public DbSet<Process> Processes { get; set; }
public DbSet<ProcessPlan> ProcessPlans { get; set; }
public DbSet<ProcessPlanStep> ProcessPlanSteps { get; set; }
public DbSet<ProductionLine> ProductionLines { get; set; }
public DbSet<StationCycleDefinition> StationCycleDefinitions { get; set; }
public DbSet<StationCycleStep> StationCycleSteps { get; set; }
public DbSet<StationDefinition> StationDefinitions { get; set; }
public DbSet<UnitOfMeasurement> UnitsOfMeasurement { get; set; }
public DbSet<Tool> Tools { get; set; }
I also tried creating "dummy" properties that are unique to each derived type. Code migrations added the new properties as columns to the table, but the migration did not create a discriminator column.
I figured out the cause of this in my situation, same as yours. The base class is abstract, therefore EF won't create a TPH table for that class since it can't be instantiated. As a result of the abstract base class, EF will create tables for each of the derived classes, and therefore no need for a discriminator column.
In my case, it was acceptable to remove abstract from the base class. Once I did this, EF's TPH worked as expected.

Introducing FOREIGN KEY constraint 'FK_dbo.Models_dbo.Makes_MakeId' on table 'Models' may cause cycles or multiple cascade paths

This is my first day I've spent exploring ASP.NET MVC 4. Specifically I'm using the Web API and obviously this issue is actually an MS SQL issue. I'm running EF migrations PM> Update-Database to get this error, but have seen it when first creating the models. My models are:
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public int MakeId { get; set; }
public virtual Make Make { get; set; }
public int ModelId { get; set; }
public virtual Model Model { get; set; }
}
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Model> Models { get; set; }
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public int MakeId { get; set; }
public virtual Make Make { get; set; }
}
The DB context is:
public class CarsContext : DbContext
{
public CarsContext() : base("name=CarsContext") { }
public DbSet<Car> Cars { get; set; }
public DbSet<Make> Makes { get; set; }
public DbSet<Model> Models { get; set; }
}
}
Would appreciate any help. My background is 5/6 solid of PHP and MySQL, so this is a steep learning curve.
Thanks.
Luke McGregor is correct. In addition to the way you fixed this you can override the default mapping that entity framework is giving you so that it doesn't cascade delete. In you CarsContext class you can override the OnModelCreating() method and specify your own mappings using fluent. This overrides what EF is trying to do by default. So you can do something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasOptional(x => x.Model)
.WithMany(y => y.Cars) //Add this property to model to make mapping work
.WillCascadeOnDelete(false);
}
This will still work with automatic migrations.
Hope that helps.

Categories