Renaming a table and a foreign key fails using ef6 - sql

I use EF6 and code first migrations. I have a two tables
public class Team {
public int Id { get; set; }
public string Name { get; set; }
}
public class TeamMember {
public int Id { get; set; }
public string Name { get; set; }
public Team Team { get; set; }
public int TeamId { get; set; }
}
I want to rename TeamMember to TeamMemberDeprecated and add a new tabled named TeamMember with some differences to the table layout. The main reason I am creating a new table is data. I want to save all of the data in the current TeamMember table so I need to rename it, and I want to transfer only the specific data I need to the new TeamMember table.
When I add the migration it looks like this
RenameTable(name: "dbo.TeamMember", newName: "TeamMemberDeprecated");
What it doesn't do is change the name of the foreign key property from FK_dbo.TeamMember_dbo.Team_TeamId to FK_dbo.TeamMemberDeprecated_dbo.Team_TeamId.
This presents a problem when I go to create the new TeamMember table because the foreign key FK_dbo.TeamMember_dbo.Team_TeamId already exists in the database.
I tried dropping the foreign key and renaming it, but this doesn't do anything and when I run the migration -verbose I see why it's expecting the foreign key to be null. I want to keep the data, but perhaps my approach is wrong.
Any suggestions are welcome, thanks.

Let EF rename the table, then go to SQL management studio and manually rename the foreign keys.
Go back to EF, make the changes, run the migration and let EF create the new foreign keys
Don't forget to backup your DB

In EF Core 5.?? there should be a few new commands (see EFCore GitHub Pull):
RenamePrimaryKey
RenameUniqueConstraint
RenameForeignKey
However, it seems that these will drop and recreate the key/constraint. In MSSQL this is not only unnecessary, but very costly. When the constraint is reapplied the database much check that each item is unique or present in the foreign table. If you can help it, do not do this.
Instead I recommend the below. You must specify the schema in the first parameter for the new name, but not in the second. The square brackets will help if any lunatic has put dots in table or constraint names:
migrationBuilder.Sql("sp_rename '[dbo].[PK_TeamMemberDeprecated]', '[PK_TeamMember]'");
It works the same for foreign keys:
migrationBuilder.Sql("sp_rename '[dbo].[FK_TeamMemberDeprecated_TeamID]', '[FK_TeamMember_TeamID]'");

Related

Problem With Inserting Record And Assigning Primary Key EF Core 3.0

I have a simple model record with a key "Id" that I'm adding to a EF Entity. It worked through Core 2.1 but now fails with the error:
SqlException: Cannot insert explicit value for identity column in table 'SpeakerRecs' when IDENTITY_INSERT is set to OFF.
The Model is defined as follows:
namespace WebAppCore.Models
{
public class SpeakerRec
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ImageUrl { get; set; }
}
}
The Code that does the insert is this and it fails on the save changes call.
foreach (var speaker in speakerRecs)
{
_context.SpeakerRecs.Add(speaker);
}
_context.SaveChanges();
I see notes about breaking changes in Core 3 and the problem is somehow around ValueGeneratedNever() but can't figure it out. I've not used EF for a while and was not planning on re-learning it. I was hoping my old code would continue to work.
If you want to manually insert the Id , you can use [DatabaseGenerated(DatabaseGeneratedOption.None)] . The None option prevents values from being generated by the database automatically in cases where they would otherwise be created :
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
If you have created the migration before , when create new migration and update database , you may get exception like To change the IDENTITY property of a column, the column needs to be dropped and recreated , you can :
Delete the actual database
Delete the Migrations folder in your project and clean the project
Create a new migration with dd-migration <migration_name> in the Package Manager Console and then Update-database .
You are inserting explicit values in the Id column, which is an identity column. To insert value explicitly into the identity column you have to turn on the identity insert.
If your database is automatically inserting value for the Id column, you should not specify them externally from your code.
Update your edmx file to reflect any changes you may have made in the database. If the database automatically assigns the value, you should see the "IsDbGenerated=true" attribute in your designer file under that property. If it's not there, you can add it manually.
Reference

SQL - REST - Storing data and retrieving it using friendly IDs

UPDATE 09/06/2018
My models looks like this:
public class Activity
{
public int Id { get; set; }
public List<Additive> Additives { get; set; }
}
public class Additive
{
public int Id { get; set; }
public int ActivityId { get; set; }
public Activity Activity { get; set; }
}
Original Question
I'm developing a RESTful API and i want to do it the cleanest way possible.
Currently i'm working on data retrieving. So i got this
If i create a new resource called activity and it has an array of objects, lets say (hardcoded, not representative):
{
"name": "act1",
"objects": [
{ "obj1":"val1" },
{ "obj2":"val2" }]
}
I'll could add or delete objects from my array.
Now, my question is, if i want to access them with an url like this:
Api/activity/1/objects/2
Which is the correct way to tell MySQL that this object is my 2th object of the list?
Or maybe i should select ALL objects of Act1 on the backend and filter them to retrieve just the 2th one.
It's pretty important that resources in your REST service have stable urls. So to figure out what 'second' is, you really need to think about what it means to be second in the list.
Are you ordering by something? If so, it's probably a bad idea to use this structure because adding a new entry can change the order and what was 'second' before might no longer be in the future.
The best thing you can do is add some kind of new id for these entries so accessing a specific resource by uri will always return that resource (or 404, etc).
I finally found the answer i were looking for.
mysql two column primary key with auto-increment
This is a MyISAM engine feature.
For MyISAM and BDB tables you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups.
In my case, this query does the job
CREATE TABLE additive (
id INT NOT NULL AUTO_INCREMENT,
activity_id INT NOT NULL,
other_column VARCHAR(30) NOT NULL,
PRIMARY KEY (activity_id, id)
) ENGINE=MyISAM;

Since EF doesn't support DELETE SET NULL, Can I run an SQL command outside EF to do it?

I know that you set a nullable key in your entity if you want that FK to be nullable:
class ChildEntity
{
// Other properties not shown for brevity
public int? ParentId { get; set; }
public virtual ParentEntity Parent; { get; set; }
}
This will result in a nullable FK. It was suggested here that we should also set the optional relationship in Fluent:
modelBuilder.Entity<ChildEntity>()
.HasOptional(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
but this still doesn't set delete set null. The FK ParentId still has delete set to No Action.
Later in the article, it was suggested that we should run the SQL command in the Seed method of the Configuration class? I'm not sure if it's a problem, but I run Update-Database quite often, and I'd be changing this setting back and forth.
So, is it safe, then, to "go behind EF's back" and change the delete rule to SET NULL in SQL Management Studio (or other app)? Since we're using SqlCommand in the seed method in plain SQL language, I want to say yes, we can go ahead and manually change the delete rule, but I'm not sure. I can't afford to experiment at this point, so I would appreciate an answer for this.
That example puts the sql in the Seed method and that means that it runs every time you call Update-Database. You avoid that by making the modification to the database using the Sql method in a migration. That way it only runs once.
public void Up()
{
Sql(#"ALTER TABLE Products DROP CONSTRAINT Product_Category");
Sql(#"ALTER TABLE Products ADD CONSTRAINT Product_Category
FOREIGN KEY (CategoryId) REFERENCES Categories (CategoryId)
ON UPDATE CASCADE ON DELETE SET NULL");"
}

Composite Foreign Key to Dynamic Table, Bad Practice

First off, I am not a DBA, I am a C# developer. I am working on a pretty complex web application and I want to start with a solid database. So here is my issue:
I am trying to create a Settings table that will hold settings for multiple objects. ie: User settings, Season settings, League settings, Team settings.
Instead of creating a table for each of those I would like to keep everything in the Settings table but I can't figure out how to create the foreign key.
My approach is to have three columns in the Settings table: TableName PrimaryKey ID. These three columns would create a composite key that would reference the appropriate object. Is my approach considered bad practice or am I on the right track? Is there a way I can do this that will still work with entity framework?
Here is my way of handling this.
In this diagram, ConfigId is primary key which is used as an FK in Person table as EyeColor, BloodType, PersonType and many more. These columns also hold a Check constraint to ensure no value apart from eye color or corresponding column get stored based on ConfigType. However this comes at a cost of broader datatype which may be smallint or int instead of tinyint.
Only difference is, I am calling these settings as ConfigType in your case {User, Team, Season etc.} and any extension can be done by inheritance concept by creating another table using ConfigId as FK in child table.
If you use inheritance, by default EF will use one table for the entire hierarchy. This is known as Table Per Hierarchy or TPH
public abstract class Setting
{
public string Key { get; set; }
public string Value { get; set; }
}
public class UserSetting : Setting
{
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class SeasonSetting : Setting
{
public int SeasonId { get; set; }
public virtual Season Season { get; set; }
}

Nhibernate delete parent in many-to-one relationship causing foreign key violation

I have a Employer object that can have multiple People:
//entities
public class Person {
public virtual Guid Id {get;set;}
public virtual string Name {get;set;}
public virtual Employer CurrentEmployer {get;set;}
}
public class Employer {
public virtual Guid Id {get;set;}
public virtual string Name {get;set}
public virtual IList<Person> Employees {get;set;}
}
//person to employer mappings
References(x => x.CurrentEmployer)
.Cascade.All()
.Column("CurrentEmployerId")
.ForeignKey("FK_Person_CurrentEmployer");
//employer to person mappings
HasMany(x=> x.Employees)
.Inverse()
.Cascade.All();
When I try to delete an employer that is linked to a person, I get a "Foreign Key violation" error.
//example
_session.Delete(oldEmployer);
How do I get nHibernate to null the CurrentEmployerId column before the employer is deleted?
try clearing all CurrentEmployer's first then deleting the Employee
public class Employer
{
public virtual Guid Id {get;set;}
public virtual string Name {get;set}
public virtual IList<Person> Employees {get;set;}
public void UnemployAll()
{
foreach(var employee in Employees)
{
employee.CurrentEmployer = null;
}
Employees = new List<Person>(); // clear it
}
}
then try the following (I think the employees (person) should all get updated), I don't know if this will work off the top of my head, but it may get you started in the right direction.
oldEmployer.UnemployAll();
_session.Delete(oldEmplorer);
Do you have code scattered all around that deletes employers? I think normally you would have only one place in the code really that deletes employers, so there wouldn't really be a need to have nhibernate do this work for you; Just have a statement there that updates all referencing employees.
If you actually do have these deletes scattered all about, then you could create an Interceptor or Event Listener that watched for deletes to the table and have the interceptor/Listener update referencing employees.
Try to do this one
//employer to person mappings
HasMany(x=> x.Employees)
.Inverse()
.Cascade.AllDeleteOrphan();
I didn't check it, but I hope it could help you.
Oracle has the cascading delete feature (based on foreign key constraints) built in. Sybase does not.
Depending on if your database supports before and after triggers you can create the functionality with before triggers. Sybase 12 doesn't have this, it only has after triggers so it's impossible on sybase. Sybase 15 has before triggers but I haven't tried it yet, but it should work, basically you write the before trigger manually to do the cascading delete.
If this before trigger functionality doesn't exist in your database, this is not possible. You would have to programmatically delete the child table rows first before deleting the parents.
That's just how it is.