EF mapping of composite key - entity-framework-4

public class MyConfiguration : EntityTypeConfiguration<MyCode>
{
public MyConfiguration()
{
HasKey(c => new { c.MyCodeId, c.EffectiveDateFrom, c.EffectiveDateTo });
Property(c => c.MyCodeId).HasColumnName("MyCode");
Property(c => c.EffectiveDateFrom).HasColumnName("MyEffectiveDateFrom");
Property(c => c.EffectiveDateTo).HasColumnName("MyEffectiveDateTo");
ToTable("My_CODES");
HasMany(d => d.MyGroups).WithRequired().HasForeignKey(k => k.MyCodeID);
}
}
This is my configuration class in EF4.
I have defined the “MyGroups” property of type IList<MyGroup>.
“MyGroup” class doesn’t have the “EffectiveDateFrom” and “EffectiveDateTo” property.
Run time following exception thrown:
System.Data.Edm.EdmAssociationConstraint: The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
Query:
How to create this kind of mapping in EF4, when all the columns used in parent is not available in the child table?

You cannot create such mapping because it is not valid. Foreign key in dependent the entity must contain all components of composite primary key defined in the principal entity.

Related

Conflict with database table and key names after migration. Asp.Net MVC 5 EF 6

This is the code that migration generates for me:
public override void Up()
{
CreateTable(
"dbo.ApplicationUserProjects",
c => new
{
ApplicationUser_Id = c.String(nullable: false, maxLength: 128),
Project_ID = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.ApplicationUser_Id, t.Project_ID })
.ForeignKey("dbo.AspNetUsers", t => t.ApplicationUser_Id, cascadeDelete: true)
.ForeignKey("dbo.ProjectTbl", t => t.Project_ID, cascadeDelete: true)
.Index(t => t.ApplicationUser_Id)
.Index(t => t.Project_ID);
}
I change the database name to something I like more, also the Key names (column names). Example the database name into "ProjectUsers", and the Keys into "ProjectID" and "UserID". So the new code looks like this:
CreateTable(
"dbo.ProjectUsers",
c => new
{
ProjectID = c.Int(nullable: false),
UserId = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.ProjectID, t.UserId })
.ForeignKey("dbo.ProjectTbl", t => t.ProjectID, cascadeDelete: true)
.ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true)
.Index(t => t.ProjectID)
.Index(t => t.UserId);
I update the database by running the migration update, the table and everything gets created with the names I gave to the database and keys. But when I navigate to the page it says:
Same thing happens when I undo the change for the Database name but keep my own names for they keys.
So why do this happen, what is happening behind the scenes that doesn't want the app to use the new names?
As shown on your exception details:
System.Data.SqlClient.SqlException: Invalid object name
'dbo.ApplicationUserProjects'
EF tried translating LINQ query statement to SQL equivalent which uses an object called ApplicationUserProjects (assumed a table), but the corresponding object doesn't exist on your DB since it was not migrated.
Assume code-first migrations in place, since you have created ProjectUsers table with different primary key index name, create proper table mapping inside overridden OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ProjectUsers>().ToTable("ProjectUsers");
}
Then, change all references related with ApplicationUserProjects to ProjectUsers table, including your model if any (e.g. if you have table attribute pointed to ApplicationUserProjects, change to ProjectUsers).
[Table("ProjectUsers")]
public class ProjectUsers
{
// entity members
}
Note that those actions above better to be done before migration begins, since you should avoid modifying auto-generated migration files.
Your problem statement also supporting primary key problem:
Same thing happens when I undo the change for the Database name but
keep my own names for they keys.
IMHO, EF model classes work with specified primary key index defined when the database created, with your own primary key field name substituted original PK field it doesn't match table-to-model relationships anymore, thus returning same exception.
Related problems:
Entity Framework throws exception - Invalid object name 'dbo.BaseCs'
Entity Framework code first seems confused on my dbo table names after I tweaked the migration file

Adding non-clustered index for multiple columns in asp.net mvc configuration

Let's say I have a table - UserToChannel_tbl which has following fields - Id, UserId and ChannelId. I am deriving EntityTypeConfiguration and putting all properties in that as below (example) -
public class UserToChannelConfiguration : EntityTypeConfiguration<UserToChannel>
{
public UserToChannelConfiguration(){
HasKey(p => p.Id);
Property(p => p.Id)
.HasColumnName("Id")
.HasDatabaseGeneratedOption((DatabaseGeneratedOption?) DatabaseGeneratedOption.Identity)
.IsRequired();
Property(p => p.UserId)
.HasColumnName("UserId").IsRequired();
HasRequired(p => p.User).WithMany().HasForeignKey(t => t.UserId);
Property(p => p.ChannelId)
.HasColumnName("ChannelId").IsRequired();
HasRequired(p => p.Channel).WithMany().HasForeignKey(t => t.ChannelId);
}
}
Now, the table already has clustered index on Id column, but I also need a non-clustered index on UserId, ChannelId column. Is there a way to achieve this here instead of doing this in Migration class or anywhere else. Seems much simpler to me if I can define all my properties at one place. I am using ASP.NET MVC 4 with EF6.
EF still cant do indexes so migrating SQL such as
CREATE NONCLUSTERED INDEX [IX_User_u_username] ON [dbo].[BlockedUsers] ([User_u_username])
is not going to happen.
The migration tool for code first is fine for the general cases, not the edge cases it seems.

( MVC3/Entity Framework) How to perform CRUD operation in a database that uses Candidate keys as foreign keys

I am a complete beginner in MVC3 and web development in general.
My client database uses unique candidate key of one table as foreign key of another. I cannot change the way the database is designed whatsoever. I have models derived from the database tables. I know that Entity Framework does not support candidate key as foreign key of another table.
So my question is, how do professionals work around this limitation of the entity framework?
Since current versions of EF require FKs to point to PKs, it's not an easy problem to overcome.
One technique I've used is (with EF CodeFirst...er...Second) to override the PKEY in the Parent table's mapping and specify an anonymous type instead.
public class ParentObject
{
public int Id {get; set;} //the actual PKEY in the Db
public string CandidateKey1 {get;set;}
public string CandidateKey2 {get;set;}
public string CandidateKey3 {get;set;}
public virtual ICollection<ChildObject> ChildObjects {get;set;}
}
public class ChildObject
{
public int Id {get; set;}
public string CandidateKey1 {get;set;}
public string CandidateKey2 {get;set;}
public string CandidateKey3 {get;set;}
public virtual ParentObject ParentObject {get;set;}
}
In order for this to work, you'll need to specify that the Parent table's PKEY is an anonymous object rather than the PKEY actually stored in the DB.
public ParentObjectMap()
{
// Primary Key
//this.HasKey(t => t.Id); //override this as PKEY for EF purposes
this.HasKey(t => new { t.CandidateKey1, t.CandidateKey2, t.CandidateKey3 });
// Table & Column Mappings
this.ToTable("ParentTable");
this.Property(t => t.Id).HasColumnName("ParentId");
this.Property(t => t.CandidateKey1).HasColumnName("Key1");
this.Property(t => t.CandidateKey2).HasColumnName("Key2");
this.Property(t => t.CandidateKey3).HasColumnName("Key3");
}
and the child object map
public ChildObjectMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("ChildTable");
this.Property(t => t.Id).HasColumnName("ChildId");
this.Property(t => t.CandidateKey1).HasColumnName("Key1");
this.Property(t => t.CandidateKey2).HasColumnName("Key2");
this.Property(t => t.CandidateKey3).HasColumnName("Key3");
this.HasRequired(t => t.ParentObject)
.WithMany(t => t.ChildObjects)
.HasForeignKey(t => new { t.CandidateKey1, t.CandidateKey2, t.CandidateKey3 });
}
Of course, this introduces other problems such as uniqueueness of the actual Parent Id property which you'll need to deal with in implemented code. However, this technique has gotten the job done for me when writing code against a similar (candidate keyed) Progress 4GL OpenEdge -> MSSQL db over which I had no control.
It's also not nearly as fast as native EF -> MSSQL mappings which take advantage of FK relationships in the DB.

How do I set Identity seed on an ID column using Entity Framework 4 code first with SQL Compact 4?

I am having some problem setting the Identity Seed on the Id column in SQL Compact 4 using the code first approach.
I have tried this
context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('Members', RESEED, 100001");
but this is not working in Sql Compact.
MyDbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
SetupMemberEntity(modelBuilder);
}
private static void SetupMemberEntity(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().Property(m => m.Id);
//.Property(m => m.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Member>().Property(m => m.FirstName).IsRequired();
modelBuilder.Entity<Member>().Property(m => m.LastName).IsRequired();
modelBuilder.Entity<Member>().Property(m => m.PinCode).IsRequired();
modelBuilder.Entity<Member>().Property(m => m.Email);
//modelBuilder.Entity<Member>().Property(m => m.DateCreated).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
//modelBuilder.Entity<Member>().Property(m => m.DateModified).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
I have added one more property MemberId to the Member entity and have tried to use
context.Database.ExecuteSqlCommand("ALTER TABLE Members ADD MemberId INT IDENTITY(10000,1) PRIMARY KEY");
but I get error message that a table can only contain one identity, but I havent set any IDENTITY so, is the Id column auto IDENTITY ?
I found the answer here:
context.Database
.ExecuteSqlCommand("ALTER TABLE Members ALTER COLUMN Id IDENTITY (10000,1)");
To set the first seed value for an Identity column using Entity Framework:
builder.Property(prop => prop.Id)
.UseIdentityColumn(10000000, 1);
from here.

Referencing foreign keys with DataMapper

I've been looking through this:
http://datamapper.org/docs/find
But haven't been able to gleam what I'm looking for, though I know it's quite simple.
I have two tables, scans and stations, with the relevant fields:
STATIONS - id (primary key), name
SCANS - id (primary key), item_id, in_station, out_station
Where in_station and out_station are foreign keys to the id field in the stations table.
I have a Scan object
class Scan
include DataMapper::Resource
property :id, Integer, :key => true
property :item_id, Integer
property :in_station, Integer
property :out_station, Integer
end
So right now, I can do Scan.all(:item_id => #barcode) to get all the scans on a particular item, and I've got the in_station id and out_station id. What's the best way of getting the names, though, instead of ids. I assume it's gotta be easier than for every scan calling Station.get(:id=> scan.in_station).
This is easy enough using SQL, but how can I alter Scan/Station to either get the name or have a property that's a Station object, so I can do something like scan.station.name?
EDIT:
I've almost got this working. I have a Station class:
class Station
include DataMapper::Resource
property :id, Integer, :key => true
property :name, String
end
and I got rid of property :in_station and property :out_station in Scan and replaced with:
belongs_to :in_station, :model => 'Station', :child_key => 'id'
belongs_to :out_station, :model => 'Station', :child_key => 'id'
Which I think/hope is saying "there's a field called in_station which is a foreign key into the Station table and one called out_station which is the same". Indeed, in_station and out_station are now instances of Station, BUT, they're the object. Even though in_station and out_station are different values, I'm getting the same object for each on every Scan. What am I doing wrong, how can I indicate that in_station and out_station are both references to Station but, when their ids are different, I expect different objects.
How about doing this:
class Station
include DataMapper::Resource
property :id, Serial
# rest of the properties
has n, :scans
end
class Scan
include DataMapper::Resource
property :id, Serial
# rest of the properties
belongs_to :station
end
Then you just do this to access the associated station:
station = Station.create
scan = station.scans.create
scan.station # returns the associated station
That should work for you at match your schema.
The assumption is that we don't want to change the underlying SQL schema. So we have to tell DataMapper to use the existing foreign key names (in_station and out_station). The twist is that DataMapper will choke if the association name is the same as the child key. That's why I have the 'my_' prefix on the association names.
class Scan
include DataMapper::Resource
#rest of the properties
belongs_to :my_in_station, :model => 'Station', :child_key => 'in_station'
belongs_to :my_out_station, :model => 'Station', :child_key => 'out_station'
end
Usage
s = Scan.get(id)
s.my_in_station.name
s.my_out_station.name

Resources