I have class like this:
class Tree
{
[Key]
public int TreeID { get; set; }
public int? ParentID { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public virtual Tree Parrent { get; set; }
public virtual ICollection<Tree> Children { get; set; }
}
And configuration class :
class TreeConfiguration : EntityTypeConfiguration<Tree>
{
public TreeConfiguration()
{
this.HasOptional(d => d.Parrent)
.WithMany(p => p.Children)
.HasForeignKey( d => d.ParentID)
.WillCascadeOnDelete(false);
}
}
It works good, but what I want is to child node use ID (from parrent node) for ParentID, not TreeID.
It should work like this:
Each node has an id - that is ID - and id of its parent - that is ParentID. TreeID is primary key and it has nothing to do with child-parent mapping - it's for database only.
I can't change the columns so I it must be this way
Well.. apparently it has something to do with the fact that FKs must always point to primary keys. So it's not possible.
Why don't you use your "ID" as a unique column - e.g. use a unique index for it?
Then you have the same semantics like you would have if ID would be the PK..
Related
Is it possible to create as a proxy element in EF Core?
For example, in the database there is the element with the id 1, which has the name Example. The second element with id 2 has no name (is null), but has a reference to element 1 ("id_replace"). In this case I would like the name returned by item 2 to be "Example" like item 1. And also the "Includes" quote to item 1 references.
The reason I have such a strange idea is that I need to have linked the elements, and if element 1 changes, the changes made are displayed on element 2 as well.
Example Registers in Database
Sure you can. Assuming that your class is:
public class YourClass
{
public int id { get; set; }
public string name { get; set; }
public int? id_replace { get; set; }
}
In your class, you need to have the one to many referencing properties:
public YourClass parent { get; set; }
public IList<YourClass> children { get; set; }
Then, in your DbContext class, in the override OnModelCreating function, you need to have a relationship set in the fluent API that indicates that id_replace is a self-referencing foreign key:
modelBuilder.Entity<YourClass>(entity =>
{
entity.HasOne(x => x.parent)
.WithMany(x => x.children)
.HasForeignKey(x => x.id_replace)
.OnDelete(DeleteBehavior.SetNull);
});
After doing that(and migrating), you have the necessary navigation properties to be able to add computed properties that do not represent anything in the database. So your class can have the property:
public int alt_name => name??$"\"{parent.name}\"";
So eventually, your class will look something like this:
public class YourClass
{
public int id { get; set; }
public string name { get; set; }
public int? id_replace { get; set; }
public YourClass parent { get; set; }
public IList<YourClass> children { get; set; }
public int alt_name => name??$"\"{parent.name}\"";
}
That way, you can discard the name property and just call on the alt_name property. You can even set the name property as private or change the names to avoid confusion.
Im using entityframework and the parent object MyObject already exist in my context (and childs loaded).
public class MyObject
{
public int id { get; set; }
public string name { get; set; }
public IEnumerable<Child> child { get; set; }
}
public class Child
{
public int id { get; set; }
public string name { get; set; }
public IEnumerable<Child2> child2 { get; set; }
}
public class Child2
{
public int id { get; set; }
public string name { get; set; }
}
I want to make a condition on a Child2 property with keeping an instance of the Parent object.
var myFiletredMyObject = myObject.child.Where(c => c.child2.Any(c2 => c2.id == 1));
This gives me back a collection of Child object, how can I do to get the parent : MyObject ?
If you've followed the entity framework code first conventions, then you should have written the relations between your tables as virtual properties.
In entity framework, the columns of your tables are represented by non-virtual properties; the virtual properties represent the relations between your tables (one-to-many, many-to-many).
Furthermore: it is better to describe the one-to-many as an ICollection<...> instead of an IEnumerable<...>. This enables you to ask for the number of children, and to Add a Child, something that entity framework can translate into SQL easily.
So with a little change in your code, changes that will give you the same database (well, I've renamed the properties a little, to make it easier in my example)
public class Parent
{
public int id { get; set; }
public string name { get; set; }
// every Parent has zero or more Children (one-to-many)
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int id { get; set; }
public string name { get; set; }
// every Child is the child of exactly one Parent, using a foreign key
public int ParentId {get; set;}
public virtual Parent Parent {get; set;}
// every Child has zero or more Child2 (one-to-many)
public virtual ICollection<Child2> Children2 { get; set; }
}
public class Child2
{
public int id { get; set; }
public string name { get; set; }
// every Child2 belongs to exactly one Child, using foreign key
public int ChildId {get; set;}
public virtual Child Child {get; set;}
}
And the DbContext:
public class FamilyDbContext
{
public DbSet<Parent> Parents {get; set;}
public DbSet<Child> Children {get; set;}
public DbSet<Child2> Children2 {get; set;}
}
This is enough for entity framework to detect your tables, the columns in the tables and the relations between the tables (in this case: one-to-many).
Because of the odd plural of Child you might get an odd table name: Childs. To prevent this, you might want to use fluent API. Add to your DbContext:
public override void OnModelCreating(DbModelBuilder modelBuilder);
{
// configure the one-to-many between Parent and Child using the foreign key:
modelBuilder.Entity<Parent>()
.HasMany(parent => parent.Children)
.HasRequired(child => child.Parent)
.HasForeignKey(child => child.ParentId);
// configure the one-to-many between Child and Child2 using foreign key
// make sure that the table gets a proper name
modelBuilder.Entity<Child>().ToTable("Children")
.HasMany(child => child.Children2)
.HasRequired(child2 => child2.Child)
.HasForeignKey(child2 => child2.ChildId);
// the one-to-many relations are configured. Set the last table name
modelBuilder.Entity<Child2>().ToTable("Children2");
}
Back to your question
If you've configured your classes the way that entity framework was meant, your query will be easy:
var myFilteredChildrenWithTheirParents = dbContext.Children
.Where(child => ...)
.Select(child => new
{
// select only the Child properties you really plan to use:
Id = child.Id,
Name = child.Name,
Parent = new
{ // again: select only the Parent properties you plan to use:
Id = parent.Id,
Name = parent.Name,
},
// no need for the foreign key, you already selected the value in Parent
// ParentId = child.ParentId,
});
Considering EntityFramework will return list of models from database you can filter your EF object like this.
var filterResult = myObject.Where(x => x.child.Any(a=>a.child2.Any(c2 => c2.id == 1))).ToList();
Filter Result will be of type MyObject.
if your Object of type MyObject you can use ternary operator to apply criteria.
var filterResult = myObject.child.Where(a => a.child2.Any(c2 => c2.id == 1)).ToList().Count > 0 ? myObject : null;
I have a class which represent a connection of a page and a tag and it looks more or less like this:
public class TagLink {
[Key]
public int Id { get; set; }
public int PageId { get; set; }
public int TagId { get; set; }
public string TagName { get; set; }
}
In my database I would like to have 2 tables: TagLinks and TagNames. First one with Id, PageId and TagId and the second one with TagId and TagName.
I would like the tag id to be a foreign key so many tag links can be assigned to a single tag name.
I gave it a try with EntityTypeConfiguration but I don't know how to configure it properly. It gives me wrong foreign keys which are built like this:
ALTER TABLE [dbo].[TagNames] WITH CHECK ADD CONSTRAINT [FK_dbo.TagNames_dbo.TagLinks_TagId] FOREIGN KEY([TagId])
REFERENCES [dbo].[TagLinks] ([TagId]);
ALTER TABLE [dbo].[TagNames] CHECK CONSTRAINT [FK_dbo.TagNames_dbo.TagLinks_TagId]
I started off with this:
public class TagLinkEntityConfiguration : EntityTypeConfiguration<TagLink>
{
public TagLinkEntityConfiguration()
{
HasKey(e => e.Id);
HasKey(e => e.TagId);
Property(e => e.Id).HasColumnName(nameof(TagLink.Id));
Property(e => e.PageId).HasColumnName(nameof(TagLink.PageId));
Property(e => e.TagId).HasColumnName(nameof(TagLink.TagId));
Property(e => e.TagName).HasColumnName(nameof(TagLink.TagName));
Map(m =>
{
m.Properties(e => new
{
e.Id,
e.PageId,
e.TagId
});
m.ToTable("TagLinks");
});
Map(m =>
{
m.Properties(e => new
{
e.TagId,
e.TagName
});
m.ToTable("TagNames");
});
}
}
How do I make it work with many to one relation? I guess the foreign key should be added on TagLinks table to reference TagId in TagNames table
Give this a try:
public class TagLink {
[Key]
public int Id { get; set; }
public int PageId { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
public class Tag {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<TagLink> TagLinks { get; set; }
}
Skip the TagLinkEntityConfiguration definitions and let EF's code-first conventions to take over and solve the problem for you.
I have 2 models:
public class Text
{
public long Id { get; set; }
public string Text { get; set; }
}
public class User
{
public int Id { get; set; }
public ICollection<Text> Texts { get; set; }
}
My model build on user is that
e.HasMany(o => o.Texts).WithOne().HasForeignKey(d => d.Id).IsRequired();
When I try to run:
dotnet ef migrations add
I get this error:
with foreign key properties {'Id' : long} cannot target the primary
key {'Id' : int} because it is not compatible. Configure a principal
key or a set of compatible foreign key properties for this
relationship.
UPDATE:
It should be able for new models to have a collection of the table Texts like:
public class Customer
{
public int Id { get; set; }
public ICollection<Text> Texts { get; set; }
}
....
e.HasMany(o => o.Texts).WithOne().HasForeignKey(d => d.Id).IsRequired();
Had similar problem using EF Core but didn't want to include the (equivalent in my class) UserId on the dependent entity Text, just to make happy EF. Finally found that you can replace the primary key used in the relationship (UserId)
using HasPrincipalKey()
modelBuilder.Entity<User>()
.HasMany(t => t.Texts)
.WithOne()
.HasPrincipalKey(u => u.Text);
Firstly, change your Model naming please,
public class Text
{
public long Id { get; set; }
public int UserId { get; set; }// add a foreign key that could point to User.Id
public string Body { get; set; }//you cannot have a string property called "Text".
public virtual User Owner { get; set; }
}
public class User
{
public int Id { get; set; }
public virtual ICollection<Text> Texts { get; set; } = new HashSet<Text>();
}
builder.Entity<Text>(table =>
{
table.HasKey(x => x.Id);
table.HasOne(x => x.User)
.WithMany(x => x.Texts)
.HasForeignKey(x => x.UserId)
.HasPrincipalKey(x => x.Id)//<<== here is core code to let foreign key userId point to User.Id.
.OnDelete(DeleteBehavior.Cascade);
});
the reason we have to figure out which key is referred is because of multiple primary keys. I saw it once in MSDN, but cannot find it back.
You can use shadow properties for foreign keys, it looks popular now.
public class Text
{
public long Id { get; set; }
public string Body { get; set; }
public virtual User Owner { get; set; }
}
public class User
{
public int Id { get; set; }
public virtual ICollection<Text> Texts { get; set; } = new HashSet<Text>();
}
builder.Entity<Text>(table =>
{
table.HasKey(x => x.Id);
// Add the shadow property to the model
table.Property<int>("UserId");
table.HasOne(x => x.User)
.WithMany(x => x.Texts)
.HasForeignKey("UserId")//<<== Use shadow property
.HasPrincipalKey(x => x.Id)//<<==point to User.Id.
.OnDelete(DeleteBehavior.Cascade);
});
In the EF context configuration, specifically in the HasForeignKey() you are supposed to specify Which property on the Text model should be the foreign key that points to the User model?
Since User model's primary key is an int, the foreign key pointing from Text to User should naturally also be an int.
I think the mistake you've made is that you are configuring the PK of Textto also be the FK for the relationship Text -> User. Try to change your Text model to :
public class Text
{
public long Id { get; set; }
public string Text{ get; set; }
public int UserId { get; set; }
}
And your configuration to:
e.HasMany(o => o.Texts).WithOne().HasForeignKey(d => d.UserId).IsRequired();
You can simply drop all the migrations or the migration that made that Id, drop the database (if it is small or has no data) and add a clean migration
I was facing the same issue in one-to-one relationship. If you are facing the issue in one-one relationship. Then try this:
public partial class document
{
public document()
{
groups = new group();
}
public int? group_id { get; set; }
public virtual group groups { get; set; }
}
[Table("group")]
public class group
{
[Key]
[Column("group_id")]
public int group_id { get; set; }
[ForeignKey(nameof(group_id))]
public virtual document document { get; set; }
}
Each document has single group. So, we can consider these settings.
modelBuilder.Entity<group>().HasOne(a => a.document)
.WithOne(y => y.groups).HasForeignKey<document>(b => b.group_id);
how do I create a collection of classes which always have a certain predefined value set in AutoFixture?
Fixture.Register<IList<Child>>(() => Fixture.CreateMany<Child>().ToList());
Say the child class has the following:
public class Child
{
public int ParentId { get; set; }
public int ChildId { get; set; }
public string ChildName { get; set; }
public int ChildValue { get; set; }
}
How do I ensure the anonymous classes always have the same parent Id whilst all other properties can be random? I guess it would also be advisable to set ChildId to 0 too as these will be pushed into a database in a repository data test.
Have you tried this?
fixture.Customize<Child>(c => c.With(x => x.ParentId, 42).With(x => x.ChildId, 0));