EntityFramework 5 CodeFirst - Get Function to Generate ID - entity-framework

I'm using CodeFirst at a new company and at my first Code Review I've been told that they don't use IDENTITY in their database tables. :( It makes me sad too, but I don't have any choice.
I'm allowed to use a simple function to generate my ID (which I then add to the DefaultValue on the Key field) - this works in SQL - but CodeFirst is just thinking I've got an ID of 0 each time.
I've tried all of the following ... can anyone assist please?
public class Test
{
[Key]
public int ID { get; set; }
}
public class Test
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
}
public class Test
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
}
public class Test
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int ID { get; set; }
}

In the end I went with ...
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
... and then overrode the SaveChanges method of the database context to catch the 0 ids ...
public override int SaveChanges()
{
var pendingChanges = ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity is IEntity);
foreach (var entry in pendingChanges)
{
var entity = ((IEntity)entry.Entity);
if (IsIDGenerated(entity)) continue;
if (entity.ID == 0) entity.ID = GetNextID(entry.Entity.GetType());
}
return base.SaveChanges();
}

Why don't you simply use a Guid then? The only reason I can think of for not using incrementing ids is when you need to migrate databases from time to time, and Guids are excellent for this.
Try this:
public class Foo {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id {get;set;}
public string Name {get;set;}
}
Or in your Context class:
modelBuilder.Entity<Foo>().Property(f => f.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

Related

Entity Framework one to many with condition

I have a Job class
[Table("Jobs")]
public class Job
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("fID")]
public int ID { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
The note class looks like this:
[Table("Note")]
public class Note
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("fID")]
public int ID{get; set;}
[Column("fld_int_NoteTypeID")]
public int NoteTypeID { get; set; }
}
Whenever I request the Notes from a job like this:
var job= context.Jobs.Include(x => x.Notes).FirstOrDefault(x => x.ID == jobId);
I would like the query to implicitly add Where NoteTypeId == 8.
Is it possible to somehow add this clause or do I have to explicitly add it each time?
What about workaround? Add NotMapped property which filters Notes collection:
[Table("Jobs")]
public class Job
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("fID")]
public int ID { get; set; }
public virtual ICollection<Note> Notes { get; set; }
[NotMapped]
public ICollection<Note> FilteredNotes
{
get
{
return Notes.Where(m => m.NoteTypeId == 8);
}
}
}
But the problem with this design is, when you select job like var job = context.Jobs.Include(x => x.Notes).FirstOrDefault(x => x.ID == jobId);, then you load all Notes to memory, then you can access filtered notes from memory like job.FilteredNotes. But when using LazyLoading it has advantage.
var job = context.Jobs.FirstOrDefault(x => x.ID == jobId);
var notes = job.FilteredNotes.ToList();
Update
You can also try Table-per-Hierarchy (TPH) mappimg. You have to create one abstract class and derived classes:
public abstract class Note
{
public int Id { get; set; }
public int NoteTypeId { get; set; }
}
public class JobNote : Note
{
}
public class OtherNote : Note
{
}
Then override OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Note>()
.Map<JobNote>(m => m.Requires("NoteTypeId").HasValue(8))
.Map<OtherNote>(m => m.Requires("NoteTypeId").HasValue(3)); // For example
}
Please note that, I do not have enough knowledge about TPH. I am just trying to show some hint. Please read further about TPH.

Only initializers, entity members, and entity navigation properties are supported when including child table in query

I've got a simple one-to-many db relationship defined by EF 6.1. The database that is generated appears correct and has the appropriate explicit relationship. However, when I try a query involving the child table, I get a NotSupportedException (see title of post). The failing code is in GetContractList (see below).
I did some digging and found some people having this problem, but those issues seemed related to attempting to include non-entity items in queries; I don't think that's what's happening here.
Anyone see what I'm doing wrong?
[Table("Contract")]
public class Contract : IContract
{
[Key,
DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContractId { get; set; }
[StringLength(1023),
Required]
public string Name { get; set; }
public DateTime DateBegin { get; set; }
public DateTime DateEnd { get; set; }
public string PhoneNumber { get; set; }
public virtual ICollection<IMarket> Markets { get; set; }
public Contract()
{
Markets = new List<IMarket>();
}
}
[Table("Market")]
public class Market : IMarket
{
[Key, Column(Order = 0)]
public int ContractId { get; set; }
[Key, Column(Order = 1)]
public int MarketId { get; set; }
}
public IEnumerable<IIdName> GetContractList(IAffiliateContractSearchCriteria criteria)
{
var now = DateTime.UtcNow;
// This is the line throwing the exception.
return _repository.AsQueryable().Where(c => (criteria.IncludeOnlyActive
? c.DateBegin < now
&& (c.DateEnd > now || c.DateEnd <= SqlDateTime.MinValue.Value)
: true)
&& (c.Markets.Any()
? c.Markets.Select(m => m.MarketId).Any(x => criteria.MarketIds.Contains(x))
: true)).OrderBy(a => a.Name).Select(a => new IdName() { Id = a.AffiliateContractId, Name = a.Name });
}
public class ContractSearchCriteria : IContractSearchCriteria
{
public bool IncludeOnlyActive { get; set; }
public List<int> MarketIds { get; set; }
public ContractSearchCriteria()
{
IncludeOnlyActive = false;
MarketIds = new List<int>();
}
public ContractSearchCriteria(bool includeOnlyActive, int[] marketIds)
: this()
{
IncludeOnlyActive = includeOnlyActive;
MarketIds.AddRange(marketIds);
}
}
Ok, so the problem is that you can't use interfaces when defining EF Entity objects and relationships.
My bad.

Database-first no primary key from database

I build my DataContext with database-first and I have this entity:
public partial class Salutation
{
public int Id { get; set; }
public string SalutationShort { get; set; }
public string SalutationLong { get; set; }
public string LetterSalutation { get; set; }
}
and I use it like this:
var s = new Salutation()
{
SalutationShort = "Mr.",
LetterSalutation = "Mister"
};
context.Salutations.Add(s);
var xx = context.SaveChanges();
Problem: I can't force the database to generate a valid Id.
The first try give's '0' as primary key, following calls results in an error because '0' is already in use.
Did i miss something?
By the way, is there any reason why
protected override void OnModelCreating(DbModelBuilder modelBuilder)
isn't called??
Thanks
Peter

Unique Key constraints for multiple columns in Entity Framework

I'm using Entity Framework 5.0 Code First;
public class Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string EntityId { get; set;}
public int FirstColumn { get; set;}
public int SecondColumn { get; set;}
}
I want to make the combination between FirstColumn and SecondColumn as unique.
Example:
Id FirstColumn SecondColumn
1 1 1 = OK
2 2 1 = OK
3 3 3 = OK
5 3 1 = THIS OK
4 3 3 = GRRRRR! HERE ERROR
Is there anyway to do that?
With Entity Framework 6.1, you can now do this:
[Index("IX_FirstAndSecond", 1, IsUnique = true)]
public int FirstColumn { get; set; }
[Index("IX_FirstAndSecond", 2, IsUnique = true)]
public int SecondColumn { get; set; }
The second parameter in the attribute is where you can specify the order of the columns in the index.
More information: MSDN
I found three ways to solve the problem.
Unique indexes in EntityFramework Core:
First approach:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>()
.HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique();
}
The second approach to create Unique Constraints with EF Core by using Alternate Keys.
Examples
One column:
modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");
Multiple columns:
modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns");
EF 6 and below:
First approach:
dbContext.Database.ExecuteSqlCommand(string.Format(
#"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})",
"Entitys", "FirstColumn, SecondColumn"));
This approach is very fast and useful but the main problem is that Entity Framework doesn't know anything about those changes!
Second approach:
I found it in this post but I did not tried by myself.
CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
true, "IX_Entitys");
The problem of this approach is the following: It needs DbMigration so what do you do if you don't have it?
Third approach:
I think this is the best one but it requires some time to do it. I will just show you the idea behind it:
In this link http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a
you can find the code for unique key data annotation:
[UniqueKey] // Unique Key
public int FirstColumn { get; set;}
[UniqueKey] // Unique Key
public int SecondColumn { get; set;}
// The problem hier
1, 1 = OK
1 ,2 = NO OK 1 IS UNIQUE
The problem for this approach; How can I combine them?
I have an idea to extend this Microsoft implementation for example:
[UniqueKey, 1] // Unique Key
public int FirstColumn { get; set;}
[UniqueKey ,1] // Unique Key
public int SecondColumn { get; set;}
Later in the IDatabaseInitializer as described in the Microsoft example you can combine the keys according to the given integer.
One thing has to be noted though: If the unique property is of type string then you have to set the MaxLength.
If you're using Code-First, you can implement a custom extension HasUniqueIndexAnnotation
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration.Configuration;
internal static class TypeConfigurationExtensions
{
public static PrimitivePropertyConfiguration HasUniqueIndexAnnotation(
this PrimitivePropertyConfiguration property,
string indexName,
int columnOrder)
{
var indexAttribute = new IndexAttribute(indexName, columnOrder) { IsUnique = true };
var indexAnnotation = new IndexAnnotation(indexAttribute);
return property.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
}
}
Then use it like so:
this.Property(t => t.Email)
.HasColumnName("Email")
.HasMaxLength(250)
.IsRequired()
.HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 0);
this.Property(t => t.ApplicationId)
.HasColumnName("ApplicationId")
.HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 1);
Which will result in this migration:
public override void Up()
{
CreateIndex("dbo.User", new[] { "Email", "ApplicationId" }, unique: true, name: "UQ_User_EmailPerApplication");
}
public override void Down()
{
DropIndex("dbo.User", "UQ_User_EmailPerApplication");
}
And eventually end up in database as:
CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
[Email] ASC,
[ApplicationId] ASC
)
The answer from niaher stating that to use the fluent API you need a custom extension may have been correct at the time of writing. You can now (EF core 2.1) use the fluent API as follows:
modelBuilder.Entity<ClassName>()
.HasIndex(a => new { a.Column1, a.Column2}).IsUnique();
You need to define a composite key.
With data annotations it looks like this:
public class Entity
{
public string EntityId { get; set;}
[Key]
[Column(Order=0)]
public int FirstColumn { get; set;}
[Key]
[Column(Order=1)]
public int SecondColumn { get; set;}
}
You can also do this with modelBuilder when overriding OnModelCreating by specifying:
modelBuilder.Entity<Entity>().HasKey(x => new { x.FirstColumn, x.SecondColumn });
For those finding this looking for a 2021 solution, the working version of the accepted answer should now look like this:
[Index(nameof(FirstColumn), nameof(SecondColumn), IsUnique = true)]
public class Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string EntityId { get; set;}
public int FirstColumn { get; set;}
public int SecondColumn { get; set;}
}
So that the annotation should live on the model and not the individual columns. Also note the nameof() syntax.
This answer was derived from the official documentation: https://learn.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations
Completing #chuck answer for using composite indices with foreign keys.
You need to define a property that will hold the value of the foreign key. You can then use this property inside the index definition.
For example, we have company with employees and only we have a unique constraint on (name, company) for any employee:
class Company
{
public Guid Id { get; set; }
}
class Employee
{
public Guid Id { get; set; }
[Required]
public String Name { get; set; }
public Company Company { get; set; }
[Required]
public Guid CompanyId { get; set; }
}
Now the mapping of the Employee class:
class EmployeeMap : EntityTypeConfiguration<Employee>
{
public EmployeeMap ()
{
ToTable("Employee");
Property(p => p.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(p => p.Name)
.HasUniqueIndexAnnotation("UK_Employee_Name_Company", 0);
Property(p => p.CompanyId )
.HasUniqueIndexAnnotation("UK_Employee_Name_Company", 1);
HasRequired(p => p.Company)
.WithMany()
.HasForeignKey(p => p.CompanyId)
.WillCascadeOnDelete(false);
}
}
Note that I also used #niaher extension for unique index annotation.
In the accepted answer by #chuck, there is a comment saying it will not work in the case of FK.
it worked for me, case of EF6 .Net4.7.2
public class OnCallDay
{
public int Id { get; set; }
//[Key]
[Index("IX_OnCallDateEmployee", 1, IsUnique = true)]
public DateTime Date { get; set; }
[ForeignKey("Employee")]
[Index("IX_OnCallDateEmployee", 2, IsUnique = true)]
public string EmployeeId { get; set; }
public virtual ApplicationUser Employee{ get; set; }
}
I assume you always want EntityId to be the primary key, so replacing it by a composite key is not an option (if only because composite keys are far more complicated to work with and because it is not very sensible to have primary keys that also have meaning in the business logic).
The least you should do is create a unique key on both fields in the database and specifically check for unique key violation exceptions when saving changes.
Additionally you could (should) check for unique values before saving changes. The best way to do that is by an Any() query, because it minimizes the amount of transferred data:
if (context.Entities.Any(e => e.FirstColumn == value1
&& e.SecondColumn == value2))
{
// deal with duplicate values here.
}
Beware that this check alone is never enough. There is always some latency between the check and the actual commit, so you'll always need the unique constraint + exception handling.
Recently added a composite key with the uniqueness of 2 columns using the approach that 'chuck' recommended, thank #chuck. Only this approached looked cleaner to me:
public int groupId {get; set;}
[Index("IX_ClientGrouping", 1, IsUnique = true)]
public int ClientId { get; set; }
[Index("IX_ClientGrouping", 2, IsUnique = true)]
public int GroupName { get; set; }
You should place Index attrib on top of the entity classs and define your multiple keys in string[]
[Index("FirstColumn", "SecondColumn", IsUnique = true, Name = "My_Unique_Index")]
public class Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string EntityId { get; set;}
public int FirstColumn { get; set;}
public int SecondColumn { get; set;}
}
I wanted to add my answer since the provided solutions did not help me. In my case one of the columns was a foreign key reference.
Old model:
public class Matrix
{
public int ID { get; set; }
public MachineData MachineData { get; set; }
public MachineVariant MachineVariant { get; set; }
}
Note that MachineVariant is an enum and MachineData is a reference.
Trying to use the provided solution by #Bassam Alugili:
modelBuilder.Entity<Matrix>()
.HasIndex(sm => new { sm.MachineData, sm.DoughVariant }).IsUnique(true);
Didn't work. So I added an ID column for the machineData foreign key like so:
public class Matrix
{
public int ID { get; set; }
public MachineData MachineData { get; set; }
[ForeignKey("MachineData")]
public int MachineDataID { get; set; }
public MachineVariant MachineVariant { get; set; }
}
And changed the modelbuilder code to this:
modelBuilder.Entity<Matrix>()
.HasIndex(sm => new { sm.MachineDataID, sm.DoughVariant }).IsUnique(true);
Which resulted in the desired solution

Retrieving the value in an association table in Entity Framework code first

I am using EF 4.1 code first and I am struggling with the association entity and getting the value that was set in the association table. I tried to follow the post on: Create code first, many to many, with additional fields in association table.
My tables are as follows (all are in plural form):
Table: Products
Id int
Name varchar(50)
Table: Specifications
Id int
Name varchar(50)
Table: ProductSpecifications
ProductId int
SpecificationId int
SpecificationValue varchar(50)
My related classes:
public class Product : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductSpecification> ProductSpecifications { get; set; }
}
public class Specification : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductSpecification> ProductSpecifications { get; set; }
}
public class ProductSpecification
{
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int SpecificationId { get; set; }
public virtual Specification Specification { get; set; }
public string SpecificationValue { get; set; }
}
My context class:
public class MyContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Specification> Specifications { get; set; }
public DbSet<ProductSpecification> ProductSpecifications { get; set; }
protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
{
}
}
My repository method where I do my call (not sure if it is correct):
public class ProductRepository : IProductRepository
{
MyContext db = new MyContext();
public Product GetById(int id)
{
var product = db.Products
.Where(x => x.Id == id)
.Select(p => new
{
Product = p,
Specifications = p.ProductSpecifications.Select(s => s.Specification)
})
.SingleOrDefault();
return null; // It returns null because I don't know how to return a Product object?
}
}
Here is the error that I am getting back:
One or more validation errors were detected during model generation:
System.Data.Edm.EdmEntityType: : EntityType 'ProductSpecification' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �ProductSpecifications� is based on type �ProductSpecification� that has no keys defined.
What does it mean that no keys are defined? Won't the ProductId and SpecificationId map to Id of Product and Id of Specification respectively?
How would I return a single product with the all the specifications for it?
Entity Framework will recognize that ProductId is a foreign key property for the Product navigation property and SpecificationId is a foreign key property for the Specification navigation property. But the exception complains about a missing primary key ("Key" = "Primary Key") on your ProductSpecification entity. Every entity needs a key property defined. This can happen either by conventions - by a specific naming of the key property - or explicity with data annotations or Fluent API.
Your ProductSpecification class doesn't have a property which EF would recognize as a key by convention: No Id property and no ProductSpecificationId (class name + "Id").
So you must define it explicitely. Defining it with data annotations is shown in the post you linked:
public class ProductSpecification
{
[Key, Column(Order = 0)]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
[Key, Column(Order = 1)]
public int SpecificationId { get; set; }
public virtual Specification Specification { get; set; }
public string SpecificationValue { get; set; }
}
And in Fluent API it would be:
modelBuilder.Entity<ProductSpecification>()
.HasKey(ps => new { ps.ProductId, ps.SpecificationId });
Both ways define a composite key and each of the parts is a foreign key to the Product or Specification table at the same time. (You don't need to define the FK properties explicitely because EF recognizes it due to their convention-friendly names.)
You can return a product including all specifications with eager loading for example:
var product = db.Products
.Include(p => p.ProductSpecifications.Select(ps => ps.Specification))
.SingleOrDefault(x => x.Id == id);