Relationship between POCO classes in Entity Framework, code first - entity-framework

I reimplementing database created automatically by SimpleMembershipProvider. Actually I have a question about 2 tables linking:
create table user_profiles
(
id int not null identity, /* PK */
username varchar(128) not null,
.........
);
create table membership
(
userid int not null, /* FK to user_profile. */
..............
);
I'd like to create relationship between initial POCO classes:
public class UserProfile : BaseType
{
public virtual Membership Membership { get; set; }
......
public string UserName { get; set; }
......
}
public class Membership
{
public virtual int UserId { get; set; }
public virtual UserProfile User { get; set; }
......
}
In Membership property UserId used as PK and in the same time as FK in database. I tried following configurations:
public class UserProfileConfiguration : EntityTypeConfiguration<UserProfile> {
public UserProfileConfiguration() {
HasKey(k => k.Id);
Map(m => m.ToTable("user_profiles"));
HasRequired(t => t.Membership)
.WithRequiredPrincipal(t1 => t1.User)
.Map(m => m.MapKey("userid"));
....
}
}
public class MembershipConfiguration : EntityTypeConfiguration<Membership> {
public MembershipConfiguration() {
HasKey(k => k.UserId);
Map(m => m.ToTable("webpages_Membership"));
//Property(x => x.UserId).HasColumnName("userid");
}
}
When line in MembershipConfiguration commented out (like in sample) command Add-Migration creates 2 records in migration command:
c => new {
UserId = c.Int(nullable: false, identity: true),
.............
userid = c.Int(nullable: false),
If I uncommenting it command failed with error message Each property name in a type must be unique. Property name 'userid' was already defined.
How could I claim required result, use column 'userid' as PK and FK in the same time?

Related

The column name is specified more than once inserting into entities with 1 to 1 relationship

I have two tables that join in a 1 to 1 relationship
using System.ComponentModel;
public class Product
{
public Product()
{
MaterialProperties = new MoreProductInfo
{
Product = this
};
}
[Key]
public int ItemId { get; set; }
[ForeignKey("ItemId")]
public virtual MoreProductInfo MaterialProperties { get; set; }
}
public class MoreProductInfo : IObjectSpaceLink, IElipseLookUp
{
[Key]
public int ItemID { get; set; }
[ForeignKey("ItemId")]
public virtual Product Product { get; set; }
}
The relationship is set up in FluentAPI like this:
modelBuilder.Entity<Product>()
.HasOne<MoreProductInfo>(x => x.MaterialProperties)
.WithOne(x => x.Product)
.HasForeignKey<MoreProductInfo>(m => m.ItemID);
When I try to save a new product I get
Microsoft.Data.SqlClient.SqlException
HResult=0x80131904
The column name 'ItemID' is specified more than once in the SET clause or column list of an INSERT. A column cannot be assigned more than one value in the same clause. Modify the clause to make sure that a column is updated only once. If this statement updates or inserts columns into a view, column aliasing can conceal the duplication in your code.
Source=Core Microsoft SqlClient Data Provider
StackTrace:
at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
I am using XAF 21.2.8 on .NET 6 and Entity Framework Core 5.0.14
I tried the following;
modelBuilder.Entity<Product>()
.HasOne(x => x.MaterialProperties)
.WithOne(x => x.Product)
.HasForeignKey<MoreProductInfo>(m => m.ItemID)
.HasForeignKey<Product>(x => x.ItemId)
.HasPrincipalKey<Product>(x => x.ItemId)
.HasPrincipalKey<MoreProductInfo>(x = >x.ItemID);
But this gives the error
System.InvalidOperationException
HResult=0x80131509
Message=The principal and dependent ends of the relationship cannot be inverted once foreign key or principal key properties have been specified.
Source=Microsoft.EntityFrameworkCore
StackTrace:
at Microsoft.EntityFrameworkCore.Metadata.Builders.InvertibleRelationshipBuilderBase..ctor(InternalForeignKeyBuilder builder, InvertibleRelationshipBuilderBase oldBuilder, Boolean inverted, Boolean foreignKeySet, Boolean principalKeySet, Boolean requiredSet)
I got it working by adjusting the attributes to use a different column name in one of the entities as follows
public class MoreProductInfo : IObjectSpaceLink, IElipseLookUp
{
[Column("ItemID")]
[Key] public int ExtItemID { get; set; }
[ForeignKey("ExtItemID")]
public virtual Product Product { get; set; }
// etc
and correcting the Fluent api
modelBuilder.Entity<Product>().HasOne(x => x.MaterialProperties).WithOne(x => x.Product)
.HasForeignKey<MoreProductInfo>(m => m.ExtItemID).HasPrincipalKey<Product>(x => x.ItemId);

EF - class split into different tables

I have an existing software that has tree tables for a single class.
users, users_a and users_b with the same primary key user_id.
How can I model such kind of tables with Entity Framework and C# - Code First, so I have one class User and all the properties from the three tables are assigned to this class?
table: users
users_id int
name nvarchar
....
table: users_a
users_id int
race_id int
......
table: users_b
users_id int
genders_id int
...
I need a class User
User
public int Id { get;set;}
public int GenderId {get;set;}
public virtual Gender Gender {get;set;}
public int RaceId {get;set;}
public virtual Race Race {get;set;}
Split the class by overriding the "OnModelCreating" method of the DbContext Class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Map(map =>
{
map.Properties(p => new
{
p.UserId,
p.Name
});
map.ToTable("users");
})
.Map(map =>
{
map.Properties(p => new
{
p.UserId,
p.GenderId
});
map.ToTable("users_b");
})
.Map(map =>
{
map.Properties(p => new
{
p.UserId,
p.RaceId
});
map.ToTable("users_a");
});
}
This structure can be implemented by using Table per Hierarchy. For details see Asp.net example

How to save ID as string in DB with Code-First EntityFramework

I have a model:
public class Something
{
public int Id { set; get; }
public string Name{ set; get; }
}
Also I have this class:
public class SomethingConfiguration : EntityTypeConfiguration<Something>
{
public SomethingConfiguration()
{
HasKey(t => t.Id).Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
Everything works fine and the Id generates automatically after inserting to DB and save changes (commit).
Now I want to add another column IdString (Id as string instead of int), so I can use it for searching-by-Id manipulations (autocompletes and more). How can I add IdString column that will get the automatic Id as string and will be save automatically while inserting and saving? Is it possible?
In Sql Server you can define a computed column with an underlying formula.
ALTER TABLE dbo.Something Add IsString AS cast(Id as nvarchar)
These columns can be mapped in you model like this.
public partial class Something
{
public int Id { get; set; }
public string IdString { get; set; }
public string Name { get; set; }
}
this.Property(p => p.IdString)
.HasMaxLength(30)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
And then used in your query.
var data1 = db.Something.Where(p => p.IdString.Contains("123"));
Because of the DatabaseGeneratedOption.Computed definition, EntityFramework will request the current Value of the column every time you update the row.
But if you only want a translatable version of ToString() for an integer value you could just use SqlFunctions.StringConvert instead.
var data = db.Something
.Where(p => SqlFunctions.StringConvert((decimal)p.Id).Contains("12"));
Update:
Add computed column with a Migration.
public override void Up()
{
CreateTable(
"dbo.Something",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 255),
})
.PrimaryKey(t => t.Id);
Sql("ALTER TABLE dbo.Something Add IsString AS cast(Id as nvarchar)");
}
public override void Down()
{
DropTable("dbo.Something");
}

code first migration add one to many relationship

I currently have a "server" entity, defined as such :
public class EntityServer
{
public int Id { get; set; }
public string Name { get; set; }
}
I wanted to add a new "Host" entity, defined as such :
public class EntityHost
{
public int Id { get; set; }
public string Name { get; set; }
public string PublicIP { get; set; }
private ICollection<EntityServer> _servers;
public virtual ICollection<EntityServer> Servers
{
get { return _servers ?? (_servers = new HashSet<EntityServer>()); }
set { _servers = value; }
}
}
So i added
public virtual EntityHost Host { get; set; }
to my server entity to link those entities with a one to many relationship
modelBuilder.Entity<EntityHost>()
.HasMany<EntityServer>(x => x.Servers)
.WithRequired(x => x.Host);
And generated a migration acordingly :
public partial class MultiHosts : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.EntityHosts",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
PublicIP = c.String(),
})
.PrimaryKey(t => t.Id);
AddColumn("dbo.EntityServers", "Host_Id", c => c.Int(nullable: false));
CreateIndex("dbo.EntityServers", "Host_Id");
AddForeignKey("dbo.EntityServers", "Host_Id", "dbo.EntityHosts", "Id", cascadeDelete: true);
}
public override void Down()
{
DropForeignKey("dbo.EntityServers", "Host_Id", "dbo.EntityHosts");
DropIndex("dbo.EntityServers", new[] { "Host_Id" });
DropColumn("dbo.EntityServers", "Host_Id");
DropTable("dbo.EntityHosts");
}
}
I've got some troubble setting a code first migration to add all it together as it outpout me a foreign key violation error when i try to access the context (which i understand as the server entity isn't linked to a host, as required by the model, because the hosts table is empty and I can't access the hosts entities to add one because of the FK violation ....)
So, my question is : how should I insert a default host entites for the existings server ?
As a trick you could first set the Server as Optional
modelBuilder.Entity<EntityHost>()
.HasOptional(x=>x.Server)
.WitMany(x => x.Hosts);
Run
Add-Migrations set_server_optional
update-Database
Update your Database and then change the Server as Required
modelBuilder.Entity<EntityHost>()
.HasRequired(x=>x.Server)
.WithMany(x => x.Hosts);
And finally
Add-Migrations set_server_required
update-Database

Entity Framework Code Only Relationship Not Being Read

(This looks like a long question, but it's not really, honest!)
I am trying to get a simple proof of concept working with Entity Framework 4 and the CTP 3 version of Code Only. It feels like I'm missing something really obvious and simple.
I have this following test which is failing:
[TestFixture]
public class ParentChildTests
{
[Test]
public void ChildRead_DatabaseContainsRelatedObjects_ParentIsNotNull()
{
var ctx = GetMyObjectContext();
var child = ctx.Children.Where(c => c.Id == 1).Single();
var parent = child.ParentTable;
Assert.That(parent, Is.Not.Null);
}
// GetMyObjectContext etc...
}
The read of child works fine and I get back a ChildTable whose ParentTableId value is '1' as I would expect, but the ParentTable property is NULL. I do not expect this because my POCOs have all virtual properties (see below) and EF4 has lazy loading enabled by default.
What am I missing?
Database
create table parent_table
(
parent_table_id int identity(1,1) primary key,
parent_table_name varchar(50) not null,
display_name varchar(50)
)
create table child_table
(
child_table_id int identity(1,1) primary key,
child_table_name varchar(50) not null,
parent_table_id int not null
)
alter table child_table add constraint FK_child_table__parent_table
foreign key (parent_table_id) references parent_table(parent_table_id)
POCO Entities
public class ParentTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string DisplayName { get; set; }
}
public class ChildTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int ParentTableId { get; set; }
public virtual ParentTable ParentTable { get; set; }
}
Entity Configurations
public class ParentTableConfiguration : EntityConfiguration<ParentTable>
{
public ParentTableConfiguration()
{
MapSingleType(pt => new
{
parent_table_id = pt.Id,
parent_table_name = pt.Name,
display_name = pt.DisplayName,
})
.ToTable("dbo.parent_table");
Property( pt => pt.Id ).IsIdentity();
Property( pt => pt.Name ).IsRequired();
}
}
public class ChildTableConfiguration : EntityConfiguration<ChildTable>
{
public ChildTableConfiguration()
{
MapSingleType(ct => new
{
child_table_id = ct.Id,
child_table_name = ct.Name,
parent_table_id = ct.ParentTableId,
})
.ToTable("dbo.child_table");
Property( ct => ct.Id ).IsIdentity();
Property( ct => ct.Name ).IsRequired();
Relationship(ct => ct.ParentTable)
.HasConstraint((ct, pt) => ct.ParentTableId == pt.Id);
}
}
(Thanks for reading this far!)
As far as understand you just do not load this navigation property.
This will result in eager loading.
var child = ctx.Children.Include("ParentTable").Where(c => c.Id == 1).Single();
Or you could enable lazy loading by setting ctx.ContextOptions.LazyLoadingEnabled = true;