how can create Unique Constraint with multi column with entityframework - entity-framework

I'm use Entityframework Code First, and an EntityTypeConfiguration using fluent API.
how can create Unique Constraint with multi column.
for example i have a table with below field
Id
CompanyId
Code
Name
i want set Code column to unique , per CompanyId

In your EntityTypeConfiguration you can do something like this:
Property(m => m.CompanyId)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_YourUniqueIndexName", 1) { IsUnique = true }));
Property(m => m.Code)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_YourUniqueIndexName", 2) { IsUnique = true }));
This will create a unique index on those 2 columns.
Make sure you use the same name for the unique index. Both need to be name "IX_YourUniqueIndex". If one is called "IX_Index1" and the other "IX_Index2" then it will create a unique index on each, which is not what you want

Related

EF Fluent API - HasIndex for complex field

I need to use Fluent API (no DataAnnotation) and should create complex Index (3 fields). I try to do it:
this.HasIndex(p => p.LoadId).HasName("IX_UniqueLoad").IsUnique();
this.HasIndex(p => p.LocationId).HasName("IX_UniqueLoad").IsUnique();
this.HasIndex(p => p.StopAction).HasName("IX_UniqueLoad").IsUnique();
but it says:
The index with name 'IX_UniqueLoad' on table 'dbo.Stop' has the same
column order of '-1' specified for columns 'LoadId' and 'LocationId'.
Make sure a different order value is used for the IndexAttribute on
each column of a multi-column index.
How to do it?
I found solution:
this.HasIndex(p => new { p.LoadId, p.LocationId, p.StopAction }).IsUnique();
it will generates:
CreateIndex("dbo.Stop", new[] { "LoadId", "LocationId", "StopAction" }, unique: true);

How to change a clustered index in Entity Framework 6.1 Code First model and apply it to an Azure database

Using the Entity Framework 6.1 code first model, what is the best way to go about changing the clustered index on a table from the default ID to another set of columns. Azure doesn't allow a table without a clustered index.
public partial class UserProfile
{
public override Guid ID { get; set; }
[Index( "CI_UserProfiles_UserID", IsClustered = true)]
public Guid UserID { get; set; }
[Required]
public Guid FieldID { get; set; }
[Required]
[StringLength(400)]
public string Value { get; set; }
}
On the table UserProfiles, ID is already the primary key and clustered index. Adding
[Index( "CI_UserProfiles_UserID", IsClustered = true)]
to UserID creates this migration:
CreateIndex("dbo.UserProfiles", "UserID", clustered: true, name: "IX_UserProfiles_UserID");
Executing the migration generates the following error:
Cannot create more than one clustered index on table 'dbo.UserProfiles'. Drop the existing clustered index
'PK_dbo.UserProfiles' before creating another.
To solve your problem, after you generate your migration file, you must modify the generated code by disabling clustered index for your primary key by assigning false as a value of clustered parameter of PrimaryKey.
After your modifications you must have something like this into your migration file:
CreateTable(
"dbo.UserProfiles",
c => new
{
Id = c.Guid(nullable: false),
UserID = c.Guid(nullable: false),
FieldID = c.Guid(nullable: false),
Value = c.String(nullable: false, maxLength: 400),
})
.PrimaryKey(t => t.Id, clustered: false)
.Index(t => t.UserID, clustered: true, name: "CI_UserProfiles_UserID");
This is not done in OnModelCreating method by using Fluent API like Manish Kumar said, but in migration file. The file that is created when you use Add-Migration command.
Existing Database
As you say in comments, your database already exist. After executing Add-Migration command, you will have this line on your DbMigration file in your Up() method:
public override void Up()
{
CreateIndex("dbo.UserProfiles", "UserID", clustered: true, name: "CI_UserProfiles_UserID");
}
You must modify the Up() method to have this code:
public override void Up()
{
this.Sql("ALTER TABLE dbo.UserProfiles DROP CONSTRAINT \"PK_dbo.UserProfiles\"");
this.Sql("ALTER TABLE dbo.UserProfiles ADD CONSTRAINT \"PK_dbo.UserProfiles\" PRIMARY KEY NONCLUSTERED (Id);");
this.CreateIndex("dbo.UserProfiles", "UserID", clustered: true, name: "CI_UserProfiles_UserID");
}
In the code above I assumed that the created clustered index is named PK_dbo.UserProfiles in your database. If not then put at this place the correct name.
This is truly an area where EntityFramwork (Core) had to advance and it still is hard.
So, I could not use IsClustered(false) for my GUID / string Primary keys, for the simple reason, the project having DbContexts was DB - agnostic. So you needed to Add EntityFrameworkCore.SqlServer and IsClustered is available then, and only.
So, my solution was simple. Add no nuget package but this attribute.
This ONLY works on EF Core.
I have tested this on SQL. Though, not sure if the other providers would allow this string not having any meaning. (e.g. SQLite does not know clustered indexes)
p.HasKey(k => k.Id).HasAnnotation("SqlServer:Clustered", false);
You need to remove the existing clustered index from your current PK 'ID' which is created by default for any "KEY" property in code first. It can be done using fluent API:
.Primarykey(x=>x.ID,clustered:false)
Once existing clustered index is removed from ID, your migration to add the clustered index on UserID should run smoothly.
After the migration file is created, modify the generated code, disabling the clustered index for the primary key by setting the clustered property to false.
Being that Azure does not allow a table without a clustered index, and there is no utility in SQL Server to 'change' a clustered index on a table, it is necessary create a new table with the clustered index and migrate the existing data to it. The code below renames the original table, migrates the data to the new table that was created with the new clustered index and drops the original table.
RenameTable("dbo.UserProfiles", "UserProfiles_PreMigrate");
CreateTable(
"dbo.UserProfiles",
c => new
{
Id = c.Guid(nullable: false),
UserID = c.Guid(nullable: false),
FieldID = c.Guid(nullable: false),
Value = c.String(nullable: false, maxLength: 400),
})
.PrimaryKey(t => t.Id, clustered: false)
.Index(t => t.UserID, clustered: true, name: "CI_UserProfiles_UserID");
Sql(#"
INSERT [dbo].[UserProfiles]
(ID,
UserID,
FieldID,
Value)
SELECT
ID,
UserID,
FieldID,
Value
FROM dbo.UserProfiles_PreMigrate
");
DropTable("UserProfiles_PreMigrate");
Any existing table constraints will be lost in this operation, so it will be necessary to recreate and indexes,foreign keys, etc on the table.

How to create index in desc order in code first?

Want to create index by EmployeeCardNumber (text) in descending order.
So the sql script should be like:
ALTER TABLE dbo.Employees ADD CONSTRAINT
IX_Employees_EmplyeeCardNumber UNIQUE CLUSTERED
(
EmployeeCardNumber DESC
)
In IndexAttribute, there is an Order property, which is an integer???
Here is my fluent api:
var indexAttr = new IndexAttribute("IX_Employees_EmplyeeCardNumber")
{
IsClustered = true,
IsUnique = true,
Order = 1 // probably should be removed since we are using a composite key.
};
Property(c => c.EmployeeCardNumber)
.HasColumnAnnotation("Index", new IndexAnnotation(indexAttr))
.HasMaxLength(8)
.IsRequired();
So the question is, how to define "DESC" in this code first approach?

How do I tell Entity (Code First) to not send the Key ID field to the database?

My code:
Models.Resource r = new Models.Resource();
r.Name = txtName.Text;
r.ResourceType = resTypes.Find(rt => rt.Name == "Content");
r.ResourceContents.Add(_resourceContent.Find(rc => rc.ID == _resourceContentID));
ctx.Resource.Add(r);
ctx.SaveChanges();
ctx.SaveChanges() causes the error:
Cannot insert explicit value for identity column in table 'Resources' when IDENTITY_INSERT is set to OFF.
Looking at what's being sent to SQL:
ADO.NET:Execute NonQuery "INSERT [dbo].[Resources]([ID], [Name], [Description], [IsOnFile],
[ContentOwnerAlias], [ContentOwnerGroup], [ResourceTypes_ID])
VALUES (#0, #1, #2, #3, #4, #5, NULL)"
My POCO Resource has ID as a Key:
public partial class Resource
{
public Resource()
{
}
[Key]
public int ID { get; set; }
And my Map code:
public class ResourceMap : EntityTypeConfiguration<Resource>
{
public ResourceMap()
{
// Primary Key
this.HasKey(t => t.ID);
How do I tell Entity to not send the Key ID field to the database?
If your PK is generated by the database (like an identity) you have to configure it in your Map.
public class ResourceMap : EntityTypeConfiguration<Resource>
{
public ResourceMap()
{
// Primary Key
this.HasKey(t => t.ID);
this.Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
You do not need the HasKey(t => t.ID) Fluent API mapping or the [Key] Data Attribute because by convention EF will assume that an integer field named ID is the key and is database generated.
As an aside, I'd recommend that when you are not following conventions you should choose one method or the other - otherwise you are repeating yourself and when you want to change something you need to change it in 2 places.
I'm not sure why the field in the database isn't already database generated - maybe when you define the field via the fluent api you have to specify that too. What I do know is that in order to make EF change a key field to be database generated you will need to drop the table.
So - rollback the migration or drop the table / database, then remove the data attribute, remove the fluent mapping and recreate.
This issue is currently on a "backlog" in the entity framework. If you want to vote for it you can do that here: Migrations: does not detect changes to DatabaseGeneratedOption
Other References:
Identity problem in EF
Switching Identity On/Off With A Custom Migration Operation

Entity Framework CTP5 Code First - Possible to do entity splitting on a non-primary key?

Using EF CTP5, I am trying to do some entity splitting where the entity is constructed from two separate tables. Is it possible to do this splitting if the key on the two tables is not the primary key?
E.g. Id is my primary key on the Note entity. I want to get my CreatedUser details from a separate table but the primary key on this second table corresponds to CreatedUserId in the Note entity.
modelBuilder.Entity<Note>()
.Map(mc =>
{
mc.Properties(n => new
{
n.Id,
n.Title,
n.Detail,
n.CreatedUserId,
n.CreatedDateTime,
n.UpdatedUserId,
n.UpdatedDateTime,
n.Deleted,
n.SourceSystemId,
n.SourceSubSystemId
});
mc.ToTable("Notes");
})
.Map(mc =>
{
mc.Properties(n => new
{
n.CreatedUserId,
n.CreatedUser
});
mc.ToTable("vwUsers");
});
I've seen comments that entity splitting is only possible if the entity primary key exists in both tables?
Thanks in advance.
Yes, all the tables that are being generated in an entity splitting scenario must have the object identifier (e.g. Note.Id) as their primary key. You should consider creating a 1:* association between User and Note entities in this case.