No tables are created after setting WillCascadeOnDelete on true - entity-framework

We are making a project in ASP MVC4, and are using code-first. This means that our database is automaticly created from our code after running it. But i noticed that optional relationships in the database dont have cascade on delete turned on by default.
So after googling, i found that you had to add it in your context, but this gives me errors.
After I set WillCascadeOnDelete on true and delete my database so the Entity Framework can create it again, it creates the database with no tables in. But if i put it on false, it works without any problems.
Here is the code i use to put it on true:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Location>()
.HasOptional(r => r.PartOfLocations)
.WithMany()
.WillCascadeOnDelete();
base.OnModelCreating(modelBuilder);
}
Here is my location model:
public class Location
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int LocationId { get; set; }
[Display(Name = "Name"), Required]
public String Name { get; set; }
[Display(Name = "Description", ResourceType = typeof(ViewRes.ClassModelStrings))]
public String Description { get; set; }
public int LocationTypeId { get; set; }
[ForeignKey("LocationTypeId")]
public virtual LocationType LocationType { get; set; }
public virtual ICollection<Location> PartOfLocations { get; set; }
public override string ToString()
{
return this.Name;
}
}
Also, if i delete the database to quick after it was created, it gives an error: The database XXX is not accessible.
And it adds " ( single user ) " after the database name, no idea if this helps

Related

How to use auto increment id with Entitiy Framework 6 and PostgreSQL not EF CORE?

I have MVC Framework Project with EF6 and POSTGRESQL. I'm using database first and create edmx file from database.
public partial class MinimalEarthEntities : DbContext
{
public MinimalEarthEntities()
: base("name=MinimalEarthEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<User> User { get; set; }
}
public partial class User
{
public long Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string GSM { get; set; }
public string Password { get; set; }
}
These code was generated from EF6. ID column is auto increment value. But EF6 can not be instert data due to auto increment not working.
Is there anybody help to solve this problem?

ef core 1.1 Autogenerated column using ValueGeneratedOnAdd

Using EfCore 1.1, I am trying to have a autogenerated column using ValueGeneratedOnAdd. The problem is i am always getting value as "0". Do i have to manually do anything with the database table ?
Here is my model
public class Contact
{
public int Id { get; set; }
// This needs to be auto generated
public Int32 ContactIndex { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public DateTime LastAccessed { get; set; }
}
This is how my OnModelCreating looks like
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>()
.Property(c => c.ContactIndex)
.ValueGeneratedOnAdd();
// I tried following as well but it did't work
// .HasDefaultValueSql("IDENTITY(int, 1,1)");
;
}
ok I figured how to do it, but i really wanted to do that without using any annotations, and i still cannot figure out how to do it without annotations on model. So here is my solution.
You need to annotate your filed in the model like following
public class Contact
{
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int32 ContactIndex { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public DateTime LastAccessed { get; set; }
}
and add OnModelCreating in your context class. This will tell entity framework to ignore the column while adding or updating records. Make sure you calling method .ValueGeneratedAddOrUpdate( ). If you use only .ValueGeneratedAdd( ) you will get errors while making updates.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>()
.Property(c => c.ContactIndex)
.ValueGeneratedOnAddOrUpdate();
;
}
Generate and run your migrations and your migrations should include "SqlServerValueGenerationStrategy.IdentityColumn"
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "ContactIndex",
table: "Contact",
nullable: false,
defaultValue: 0)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
}

Cannot Insert duplicate key on related table

My model has Owners and Complexes. An owner can have many complexes, and a complex could theoretically have multiple owners (joint ownership). I want to be able to create new complexes and owners independently, so neither should require the other. However, when I try to add a new complex, I get this error:
Violation of PRIMARY KEY constraint 'PK_dbo.Owners'. Cannot insert duplicate key in object 'dbo.Owners'. The duplicate key value is (fcd72b09-b1ef-4894-83de-cb4897c0c401).
The statement has been terminated.
For the record, there is currently one existing owner (with the ID mentioned in the error). The owner is already associated with another complex. I should be able to add a new complex with this owner, but obviously it's not allowing me to.
What do I need to change with my model to accomodate this? Relevant code follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//modelBuilder.Entity<Complex>().ToTable("Complex");
//modelBuilder.Entity<Unit>().ToTable("Unit");
//modelBuilder.Entity<Address>().ToTable("Addresses");
//modelBuilder.Entity<Tenant>().ToTable("Tenant");
modelBuilder.Entity<ContactInfo>().ToTable("Contacts");
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Entity<Complex>()
.HasOptional(x => x.Owner)
.WithMany(x => x.Complexes);
modelBuilder.Entity<Unit>()
.HasOptional(x => x.Complex)
.WithMany(x => x.Units);
modelBuilder.Entity<Owner>()
.HasMany(x => x.Complexes);
base.OnModelCreating(modelBuilder);
}
}
Owner and Complex models:
public class Owner
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? ContactInfoId { get; set; }
[ForeignKey("ContactInfoId")]
public ContactInfo ContactInfo { get; set; }
public ICollection<StaffMember> Employees { get; set; }
public ICollection<Complex> Complexes { get; set; }
public Owner()
{
this.Id = System.Guid.NewGuid();
this.Employees = new HashSet<StaffMember>();
this.Complexes = new HashSet<Complex>();
}
public void AddEmployee(StaffMember employee)
{
Employees.Add(employee);
}
public void AddComplex(Complex complex)
{
Complexes.Add(complex);
}
}
public class Complex
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? OwnerId { get; set; }
[ForeignKey("OwnerId")]
public Owner Owner { get; set; }
public Guid? AddressId { get; set; }
[ForeignKey("AddressId")]
public virtual Address Address { get; set; }
public virtual ICollection<Unit> Units { get; set; }
public virtual ICollection<StaffMember> StaffMembers { get; set; }
public Complex()
{
this.Id = System.Guid.NewGuid();
this.Units = new HashSet<Unit>();
this.StaffMembers = new HashSet<StaffMember>();
}
public void AddUnit(Unit unit)
{
Units.Add(unit);
}
public void AddStaff(StaffMember staffMember)
{
StaffMembers.Add(staffMember);
}
}
Your entities aren't setup correctly. In your Complex object, you are stating that it has only 1 owner so you're setting it up as a one to many instead of a many to many. If you set it as a collection instead of an object, EF will handle the many to many table for you

EF code first telling me to do the migration for db object which is already is in db

i am working with EF code first. so initially i have no tables in database. so i wrote some class and when query those class then i saw EF code first create those tables in db but when i create sql server view in db and later map that view with my code in c# & EF project and when i try to query that view then i was getting error message as follows.
Additional information: The model backing the 'TestDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database
i understand that EF is telling me to do the migration but if i migrate then EF will create that view in db again when the view is in db already exist.
so tell me how could i inform EF that my view is already is in db so migration is not required.
please guide me. thanks
EDIT 1
first time my database has no table. so i wrote some classes like below one.
public class CustomerBase
{
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
}
public class Customer : CustomerBase
{
public virtual List<Addresses> Addresses { get; set; }
}
public class Addresses
{
[Key]
public int AddressID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public bool IsDefault { get; set; }
public virtual List<Contacts> Contacts { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
public class Contacts
{
[Key]
public int ContactID { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
public bool IsDefault { get; set; }
public int AddressID { get; set; }
public virtual Addresses Customer { get; set; }
}
public class TestDBContext : DbContext
{
public TestDBContext()
: base("name=TestDBContext")
{
}
public DbSet<Customer> Customer { get; set; }
public DbSet<Addresses> Addresses { get; set; }
public DbSet<Contacts> Contacts { get; set; }
}
when i query the customer like below query then EF create all required tables in db behind the curtains.
var bsCustomer = (from cu in db.Customer
where (cu.CustomerID == 2)
select new
{
cu,
Addresses = from ad in cu.Addresses
where (ad.IsDefault == true)
from ct in ad.Contacts
select ad,
}).ToList();
later i create a view in db and refer that view in code like below one.
public partial class vwCustomer
{
[Key]
public int CustomerID { get; set; }
public string FirstName { get; set; }
}
public class vwCustomerConfiguration : EntityTypeConfiguration<vwCustomer>
{
public vwCustomerConfiguration()
{
this.HasKey(t => t.CustomerID);
this.ToTable("vwCustomers");
}
}
so now my DbContext look like below one with view class reference
public class TestDBContext : DbContext
{
public TestDBContext()
: base("name=TestDBContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new vwCustomerConfiguration());
}
public DbSet<Customer> Customer { get; set; }
public DbSet<Addresses> Addresses { get; set; }
public DbSet<Contacts> Contacts { get; set; }
public virtual DbSet<vwCustomer> vwCustomers { get; set; }
}
Error occur the moment i try to query the view
using (var db = new TestDBContext())
{
var listMyViews = db.vwCustomers.ToList();
}
the error was Additional information: The model backing the 'TestDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database
thanks
Another way we can do it and it solve my problem. see the code.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<YourDbContext>(null);
base.OnModelCreating(modelBuilder);
}
code taken from here https://stackoverflow.com/a/6143116/6188148
we can follow this approach too.
public partial class AddingvwCustomer : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
i guess this will works too but not tested myself.
we can use the Fluent API to configure it using the Ignore method:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<MyClass>();
}
Add new migration as normally and from the migration code in Up (and Down) method remove code that tries to create new table manually (call to CreateTable method in Up and DropTable in Down). Then apply migration to your db and everything works perfectly.
Unfortunately automatic migration generation is not very intelligent tool and very often one need to manually specify how the database should be altered. In the documentation for EF migrations it is stated that it is perfectly fine to edit manually migrations code.

Add an entity to your initial migration

I am using code first migrations in an asp.net mvc program.
I am using the default authenticion and roles which is provided in the project.
Now when I enabled migrations it automatically generated a migration class which generates all of the tables etc.
Here is an excample of the specific table which I wqish to edit.
CreateTable(
"dbo.AspNetUserRoles",
c => new
{
UserId = c.String(nullable: false, maxLength: 128),
RoleId = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.UserId, t.RoleId })
.ForeignKey("dbo.AspNetRoles", t => t.RoleId, cascadeDelete: true)
.ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true)
.Index(t => t.RoleId)
.Index(t => t.UserId);
Now I would like to add a description field to this table. It would be really easy if i just added it in the database but then I will loose my code first migrations.
1> Where does Entity framework get all its commands for the initial migration? Because there are no models in my project that I can see which specify the tables it creates.
2> How can I modify or edit some of the original tables which are generated? I have tried just editing the initial migrations folder but that does not work?
(Just my thinking) Is it not that maybe the Roles and users models are stored in the framework and that is where it gets the structure of the tables from? If so Can i not extend the default model to add more attributes? Cause I know you can do it for the ApplicationUser, I have done so before, here is an example of it:
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
[Required]
[MaxLength(50)]
[Display(Name = "Email Address")]
public string email { get; set; }
}
Thats how i can add a email address field to the default user. Can I not maybe do this with the roles as well.
You dit it perfectly for the ApplicationUser, just continue like that for the other stuff. If you want to add description to the table of the users. Just add it to the ApplicationUser model. Don't mind the foreign keys and virtual properties.
public class ApplicationUser : IdentityUser
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string GroupName { get; set; }
[Required]
public string Email { get; set; }
[Required]
[StringLength(15)]
public string Phone { get; set; }
public string Remark { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidUntil { get; set; }
// Foreign keys
[ForeignKey("Bank")]
public string AccountNumber { get; set; }
[ForeignKey("Address")]
public int? AddressId { get; set; }
public virtual Bank Bank { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Request> Requests { get; set; }
}
For editing the role class, you should inherit IdentityRole on your class and add the properties:
public class ApplicationRole : IdentityRole
{
public string Description { get; set; }
}
The framework will generate new migration classes which will be ran when you use the Update-Database command.
You have to change your identityManager (Use ApplicationRole here):
public class IdentityManager
{
public bool RoleExists(string name)
{
var rm = new RoleManager<ApplicationRole>(new RoleStore<ApplicationRole>(new ApplicationDbContext()));
return rm.RoleExists(name);
}
public bool CreateRole(string name)
{
var rm = new RoleManager<ApplicationRole>(new RoleStore<ApplicationRole>(new ApplicationDbContext()));
var idResult = rm.Create(new ApplicationRole(name));
return idResult.Succeeded;
}
}
You have to overwrite the Role in ApplicationDbContext doing like following (don't forget the new):
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
...
public DbSet<Product> Products { get; set; }
new public DbSet<ApplicationRole> Roles { get; set; }
}