How does migrationBuilder determine which indexes to create? - asp.net-core

This is using asp.net core 2.0, EF, visual studio 2017, sql server 2016, and creating a db migration via package manager console using 'add-migration' tool within the Package Manager Console.
I have a simple many-to-many relationship configured as below, 2 tables and a third 'joining table':
public class TblTrack
{
public int ID { get; set; }
...
//Navigation properties
public List<TblProductItem> ProductItems { get; set; }
}
public class TblProduct
{
public int ID { get; set; }
...
//Navigation properties
public List<TblProductItem> ProductItems { get; set; }
}
public class TblProductItem
{
[Key]
[Required]
public int ProductID { get; set; }
[Key]
[Required]
public int TrackID { get; set; }
//Navigation properties
public TblProduct Product { get; set; }
public TblTrack Track { get; set; }
}
This is from the migration (generate in PMC) to create the joining table:
migrationBuilder.AddPrimaryKey(
name: "PK_tbl_ProductItems",
table: "tbl_ProductItems",
columns: new[] { "ProductID", "TrackID" });
migrationBuilder.CreateIndex(
name: "IX_tbl_ProductItems_TrackID",
table: "tbl_ProductItems",
column: "TrackID");
Please could someone explain:
What's the purpose of the index IX_tbl_ProductItems_TrackID?
Why was an index created for TrackID but not for ProductID?
Is there some other setting that determines which indexes will be created in the migration?

By default EF automatically creates Index (non-unique) on each property that is a foreign key reference.
Make sure that EF correctly created relation between TblProduct and TblProductItem(for example in SQL Server by expanding keys) - if not, specify relation explicitly using Fluent Api.
Regarding other setting you can require creating indexes using method in your Context class, but that index should be auto generated if foreign key relation is set.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TblProductItem>()
.HasIndex(e => e.TrackID);
}

I'm struggling with the same thing. I found out that if I reversed the order of the keys (using Fluent, however) it would make an index for the second column instead.
So to me, it seems like there's a bug in the framework when using composite keys. It is the ForeignKey annotation that causes the index to be made, but in this process it seems to believe that the primary key is the FIRST column only (in that case, no extra index needed for a "primary key" column), so it only creates an index for the second. But the primary key is composite, so it should probably create an index for the first column too.
A workaround (if you really want the first column to be indexed too) is to do what's suggested in the accepted answer. Should the bug (AFAIK) be fixed later, I don't think it would cause a problem by trying to create an extra index or anything.

Related

Why is SQL Server warning me this may cause cycles or multiple cascade paths? [duplicate]

I'm using Entity Framework - Code First, and I've run into an issue where I have to disable cascading deletes for a particular foreign key.
Here is my entity class:
public class ChallengeMatch
{
public int Id { get; set; }
public int ChallengerClubMemberId { get; set; }
public int ChallengeeClubMemberId { get; set; }
public bool ChallengerWon { get; set; }
public string Score { get; set; }
public virtual ClubMember ChallengerClubMember { get; set; }
public virtual ClubMember ChallengeeClubMember { get; set; }
}
If I allow Code First to generate the database for this table with all default settings (which includes cascading deletes), it will throw an exception when it tries to generate the database objects.
If I add the following code to my DbContext class, I no longer get an exception, but now cascading deletes will only somewhat work:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ChallengeMatch>()
.HasRequired(cm => cm.ChallengeeClubMember)
.WithMany()
.HasForeignKey(cm => cm.ChallengeeClubMemberId)
.WillCascadeOnDelete(false);
}
Because of this, I have to write code by hand. Specifically, if I'm about to delete ClubMember 13, I have to delete any ChallengeMatch whose ChallengeeClubMemberId is 13.
What I don't understand is why any of this should be necessary. Why can't SQL Server handle a cascading delete even if there are two foreign keys pointing to the same table? I can't think of any reason why this would fail.
It should be a simple three-step process (psuedo code):
Delete all challenge matches where ChallengerClubMemberId == 13
Delete all challenge matches where ChallengeeClubMemberId == 13
Delete the club member where Id = 13.
Why can't SQL Server do this or why does it choose not to?
The problem is that your configuration allows multiple cascade delete paths. This can happen if both your properties will point to the same ClubMember. SQL Server doesn't allow that. It is more about internal implementation of SQL server and more details can be found in this answer. I think that this check is simple and safe solution to avoid some race conditions during deletes cascading in parallel.

Entity Framework Core Code First Model Navidation Properties

I have a Web API that uses entity framework. I have several tables there were created using the code first setup. My Competitions class is defined below.
Everything works great and I'm able to get my Competitions table data along with all the data in the navigation properties that are returning a collection. However, I'm not able to get any values for the CompetitionTypes and Users navigation properties. OwnerId references UserId in the Users table.
How would I get the linked data in my CompetitionTypes and Users table? I basically want the same thing as the three collection navigation properties, except that CompetitionTypes and Users would only return one row.
public partial class Competitions
{
[Key, Required]
public int CompetitionId { get; set; }
public int CompetitionTypeId { get; set; }
public int OwnerId { get; set; }
public string CompetitionName { get; set; }
public CompetitionTypes CompetitionTypeId { get; set; }
public Users UserId { get; set; }
public ICollection<Participants> Participants { get; set; }
public ICollection<ResultStats> ResultStats { get; set; }
public ICollection<Results> Results { get; set; }
}
}
EF auto-matches FK properties with navigation properties based on conventions. Namely, it expects FK properties to be named the same as navigation properties, just with Id at the end. In other words, for it to automatically match up OwnerId, you'd need a navigation property like:
public User Owner { get; set; }
Since your navigation property is UserId, it's actually looking for a property named UserIdId.
If you don't want to follow conventions, then you must either use the ForeignKey attribute or fluent config to tell EF which property belongs with which.
That said, there's some pretty major issues with your naming of things here. First, entities should always be singular User, not Users. Second, you should not have navigation properties that end with Id: e.g., User, not UserId. Only actual PK or FK properties should end with with Id. Finally, don't prefix properties on your entity with the entity name. This last one is mostly for readability. Which is more natural: competition.Id or competition.CompetitionId? Likewise with CompetitionName; it should just be Name. And, for what it's worth, you don't need Required for either a primary key or a non-nullable type (such as int). In either case, the property is required by default.

Auto generate GUID in mvc5 that uses EF

I have an mvc5 application that is connected to a EF database. Some fields in this database are meant to be autogenerated as declared in SQL, but when used in MVC and upon inserting records, the GUID only contains the value of 0 for all records. How can I resolve this? Any help will be appreciated. Thanks.
Model class:
public partial class Store
{
public int StoreID { get; set; }
public int CustomerID { get; set; }
public string StoreName { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public System.Guid StoreUID { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int StoreNumber { get; set; }
public string StoreLogo { get; set; }
public string StoreLogoPath { get; set; }
public string StoreAddress { get; set; }
public string StoreCity { get; set; }
public string StoreRegion { get; set; }
public string StoreCountry { get; set; }
public virtual Customer Customer { get; set; }
}
Both StoreUID and StoreNumber supposed to be autogenerated fields. Below is an example how its supposed to be when a new store is inserted, however currently, storeNumber and StoreUID both just return 0.
You need to add defaults to your database table to generate the fields.
ALTER TABLE [dbo].[Store] ADD DEFAULT (newid()) FOR [StoreUID]
ALTER TABLE [dbo].[Store] ADD DEFAULT (myfuncthatreturnsanint()) FOR [StoreNumber]
This isn't really an Entity Framework feature. EF needs to be aware of these column types to generate the appropriate SQL. What you require is something that's actually achieved from the database. For Model First, I got the auto generated int Id functionality by modifying the T4 template that ships with EF to write the appropriate SQL, but it really is database functionality. StoreNumber is a different case since SQL server only allows one identity column.
For your database, your StoreUID column specification should be:
StoreUID uniqueidentifier not null DEFAULT newid()
You don't specify if you're dealing with model first or code first, or if you're building something new, so you may have to modify your existing table for this.
EDIT
If you're using model first, ensure that in your model the Store Generated Column is set to Identity for the StoreUID value to be server generated. If not, and you're not worried about who/what creates the GUID, then create a default constructor for Store, if you don't already have one. Then in there add StoreUID = Guid.NewGuid();.
For StoreNumber, SQL server doesn't support multiple columns with auto incrementing integers. You'd need to research a number of strategies for inserting it.
A number are listed here and here. Essentially make StoreNumber a function of StoreID with Computed Columns, or use an independent Sequence:
ALTER TABLE Store DROP COLUMN StoreNumber;
GO
ALTER TABLE Store ADD StoreNumber AS StoreID + 550000;

Generating Primary key without set it as key

When I create a table in entity framework, i am not set any key in the table. But, when the table created the id field in the table set as key, Why?
public class EntityBase
{
public long Id { get; set; }
public DateTimeOffset? DeletedOn { get; set; }
public string RefId { get; set; }
public DateTimeOffset CreatedOn { get; set; }
}
As mentioned here Entity framework automatically identifies the primary key based on ideal naming like Id in your case :-
As detailed in document :-
Other default conventions supported by EF include the ability to
automatically identify primary-key and foreign keys based on common
naming patterns (for example: an ID or DinnerID property on the Dinner
class will be inferred as the primary key). EF also includes smart
conventions for wiring-up association relationships between models.
For more information :-
http://blogs.msdn.com/b/efdesign/archive/2010/06/01/conventions-for-code-first.aspx

Composite primary key scenario

I need a many to many structure, but with an aggregate constraint.
What i'm trying to accomplish is done easily in pure sql, but since ActiveRecord discourages composite primary keys, i'm not sure how to accomplish what i need in recommended style.
Here is what i would have in pure sql:
table Project (ID int)
table Report (ProjectWideID nvarchar(50), ProjectID int, primary key (ProjectWideID, ProjectID))
table ChosenReport(ListOrder int, ProjectWideReportID, ReportID, primary key (ProjectID,ProjectWideReportID))
This means that a project has many reports.
Each report has an assigned id, which is unique inside a project.
Project has many chosen reports as an ordered list, each of them references a report in the same project by it's project-wide assigned report id.
But here is my ActiveRecord classes, and something is missing here.
[ActiveRecord]
public class Project
{
[PrimaryKey]
public int ID { get; set; }
[HasMany] IList<Report> Reports { get; set; }
[HasMany] IList<ChosenReport> ChosenReports { get; set; }
}
[ActiveRecord]
public class Report
{
[PrimaryKey]
public int ID { get; set; }
[BelongsTo("ProjectID")]
public Project ParentProject { get; set; }
// ... other properties
}
[ActiveRecord]
public class ChosenReport
{
// This one must be a key property
[BelongsTo("ParentProjectID")]
Project ParentProject { get; set; }
// This one must be a key property
[BelongsTo("ParentProjectID")]
Report ParentReport { get; set; }
// ... other properties
}
Now, since i have surrogate keys, i don't know how to constraint ChosenReport so it can't have reference to a report from different project. So i have to enforce constraints in domain. Do i have any other options for this with ActiveRecord?
The true ActiveRecord way of doing this would be making your classes inherit from ActiveRecordBase<T> and then overriding OnSave() and implementing your checks there. But I recommend implementing the checking logic in a NHibernate interceptor or event listener instead.