Problem: I'm able to save to a self referencing collection but Entity Framework does not show them in the collection after saving to the database.
Desired: Access entities in the collection by {entity}.{collection}.{query()};
Entity:
class Feat
{
public Feat()
{
PrerequisiteFeats = new HashSet<Feat>();
}
public int Id { get; set; }
// Other properties here
public virtual ICollection<Feat> PrerequisiteFeats { get; set; }
}
Context:
class PathfinderContext : DbContext
{
public DbSet<Feat> Feats { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Feat>()
.HasMany(feat => feat.PrerequisiteFeats)
.WithMany()
.Map(m =>
{
m.MapLeftKey("FeatId");
m.MapRightKey("PrerequisiteFeatId");
m.ToTable("PrerequisiteFeats");
});
}
}
feats.Include("PrerequisiteFeats").SingleOrDefault(x => x.Id == 2)
This will basically query both Feats and Prerequisite Feats in the same query. It's combining 2 seperate queries into one.
Related
I am very new to Entity Framework Core (Entity Framework in General), and I have watched several tutorial videos and done a Pluralsight course.
I am using ASP.NET Web API, and I want to add a new entity, that has a One-to-Many relationship. The models are as follows:
"Parent" Class:
public partial class VerificationVoltage
{
public int Id { get; set; }
public DateTime Date { get; set; }
public string Type { get; set; }
public int VerificationVoltageSerialId { get; set; }
public VerificationVoltageSerial Serial { get; set; }
}
"Child" Class:
public class VerificationVoltageSerial
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(20)]
public string Serial { get; set; }
public List<VerificationVoltage> VerificationVoltage { get; set; }
}
I would like the VerificationVoltageSerial.Serial to be unique, and that the database will first check whether or not the serial exists, before adding the newly added serial (The serials themselves are unique).
Is there a way to do the following: If a row with the same serial exist, as what needs to be added, entity framework then automatically selects the Serial and SerialId and populates the Entity that is being added, with the serial selected from the database.
This is what I am doing currently, I feel that this is a very manual check, an maybe there is something more automatic: (i.e I want it to wrok, even if I remove the IF Statement and the exisitngEntry query in the controller)
[HttpPost]
public async Task<VerificationVoltage> AddVerificationVoltage(VerificationVoltage verificationVoltage)
{
var exisitngEntry = await repository.GetBySerialNoTracking(verificationVoltage.Serial.Serial)
.ConfigureAwait(false);
if (exisitngEntry != null)
{
var existingSerial = exisitngEntry.Serial;
verificationVoltage.Serial.Id = existingSerial.Id;
verificationVoltage.Serial = new VerificationVoltageSerial()
{
Id = existingSerial.Id,
Serial = existingSerial.Serial,
};
}
var addedEntry = repository.Add(verificationVoltage);
await repository.SaveChanges().ConfigureAwait(false);
return addedEntry;
}
public async Task<VerificationVoltage> GetBySerialNoTracking(string serialNumber)
{
return await DbSet.Include(a => a.Serial)
.Where(a => a.Serial.Serial.Equals(serialNumber))
.OrderBy(a => a.VerificationVoltageSerialId)
.AsNoTracking()
.FirstOrDefaultAsync()
.ConfigureAwait(false);
}
My protected override void OnModelCreating(ModelBuilder modelBuilder) method, only repeats the attributes:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ValidationLabAmbientMeasurements>()
.HasOne(a => a.AmbientMeasurementsIdentifier)
.WithMany();
modelBuilder.Entity<VerificationVoltage>()
.Property(a => a.Id)
.ValueGeneratedOnAdd();
modelBuilder.Entity<VerificationVoltage>()
.HasOne(a => a.Serial)
.WithMany(nameof(VerificationVoltage));
modelBuilder.Entity<VerificationVoltage>()
.HasMany(a => a.VerificationVoltageMeasurements)
.WithOne(nameof(VerificationVoltage));
}
I have tried searching for answers, but my search queries do not get results on this specific issue.
Ok I think I may have got this wrong in the model builder. I want the case which can be one case but inside that one case they can be many relationships.
modelBuilder.Entity<RelationShips>()
.HasOne<MISObject>(s => s.Case)
.WithMany(g => g.RelationShip)
.HasForeignKey(s => s.MisObjectId);
But when I attempt to save with the above
DbUpdateException: An error occurred while updating the entries. See
the inner exception for details. SqlException: The INSERT statement
conflicted with the FOREIGN KEY constraint
"FK_RelationShips_MISobject_MisObjectId". The conflict occurred in
database "MISSystem", table "dbo.MISobject", column 'Id'. The
statement has been terminated.
My MISObject has a collection of relationships as such
public class MISObject {
public int Id { get; set; }
public ICollection<RelationShips> RelationShip { get; set; }
}
public class RelationShips {
public int Id { get; set; }
public MISObject Case { get; set; }
}
Will the above not allow a one to many relationship basically one case could have 20 relationships but there could be many cases. But those 20 relationships should only belong to that case if that makes since.
I think I need to use hasmany but not to sure how in this context.
Edit 2
I think this might be what i need but im not sure how legal it is.
modelBuilder.Entity<MISObject>()
.HasMany(c => c.RelationShip);
modelBuilder.Entity<RelationShips>()
.HasMany(c => c.PersonOfIntrests);
modelBuilder.Entity<POI>()
.HasMany(c => c.PersonOfIntrestsPhotos)
.WithOne(e => e.PersonOfIntrest);
To make this work with the FluentAPI, you need to completely define the navigation and use a foreign key:
public class Relationship {
public int Id { get; set; }
public int MisObjectId { get; set; } // <-- add foreign key ID
public MisObject Case { get; set; }
}
public class Context : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MisObject>()
.HasMany(o => o.Relationships)
.WithOne(r => r.Case) // <-- add other endpoint
.HasForeignKey(r => r.MisObjectId); // <-- add FK property
}
}
Here is a fully working console project, that demonstrates these concepts:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class MisObject {
public int Id { get; set; }
public ICollection<Relationship> Relationships { get; set; }
}
public class Relationship {
public int Id { get; set; }
public int MisObjectId { get; set; } // <-- add foreign key ID
public MisObject Case { get; set; }
}
public class Context : DbContext
{
public DbSet<MisObject> MisObjects { get; set; }
public DbSet<Relationship> Relationships { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(#"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So62854210")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MisObject>()
.HasMany(o => o.Relationships)
.WithOne(r => r.Case) // <-- add other endpoint
.HasForeignKey(r => r.MisObjectId); // <-- add FK property
// Technically not necessary, because this navigation has already been defined
// in the previous line, but might be good practice anyway.
modelBuilder.Entity<Relationship>()
.HasOne(r => r.Case)
.WithMany(o => o.Relationships)
.HasForeignKey(r => r.MisObjectId);
}
}
internal static class Program
{
private static void Main()
{
using (var context = new Context())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var newMisObject = new MisObject();
var newRelationships = new[]
{
new Relationship {Case = newMisObject},
new Relationship {Case = newMisObject},
new Relationship {Case = newMisObject},
};
context.MisObjects.Add(newMisObject);
context.Relationships.AddRange(newRelationships);
context.SaveChanges();
}
using (var context = new Context())
{
var misObjects = context.MisObjects
.Include(o => o.Relationships)
.ToList();
Debug.Assert(misObjects.Count == 1);
Debug.Assert(misObjects[0].Relationships.Count == 3);
}
}
}
}
Coincidentally, because the properties in this example follow EF Core naming conventions, you could even remove the OnModelCreating() method entirely here and the result would still work.
See Relationships: Conventions for further information on that.
Just use convention there is no need to use fluent config for relationships:
public class MISObject
{
public int Id { get; set; }
public ICollection<RelationShips> RelationShip { get; set; }
}
public class RelationShips
{
public int Id { get; set; }
public int CaseId { get; set; }
public MISObject Case { get; set; }
}
without any error, EF Core makes your Tables in the database.
I have introduced a many to many relationship between two of my existing tables. For this, I have added a third table, which contains only the Ids of the other two tables.
Since I am using EF, I have also added
public virtual List<EntityOne> EntityOnes in EntityTwo
and
public virtual List<EntityTwo> EntityTwos in EntityOne.
However, with this, when I get the EntityTwo object, it does not contain the associated EntityOne object. The list has a count of zero, even though the data is there in the tables.
Am I missing something here? Is there anything else, I need to do?
Not sure,if this is relevant, but I have also this in OnModelCreation
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<EntityOne>().
HasMany(p => p.EntityTwos).
WithMany(a => a.EntityOnes).
Map(
m =>
{
m.MapLeftKey("EntityTwoId");
m.MapRightKey("EntityOneId");
m.ToTable("EntityRelations");
});
////Make sure a context is not created by default.
}
Try this:
public partial class One
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
private ICollection<OneTwo> _oneTwos;
public virtual ICollection<OneTwo> OneTwos
{
get { return _oneTwos ?? (_oneTwos = new List<OneTwo>()); }
set { _oneTwos = value; }
}
}
public partial class Two
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
private ICollection<OneTwo> _oneTwos;
public virtual ICollection<OneTwo> OneTwos
{
get { return _oneTwos ?? (_oneTwos = new List<OneTwo>()); }
set { _oneTwos = value; }
}
}
Add navigation properties to the join class:
public partial class OneTwo
{
public virtual int OneId { get; set; }
public virtual int TwoId { get; set; }
public virtual One One { get; set; }
public virtual Two Two { get; set; }
}
Add composite key to the join class and configure relationships:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<OneTwo>() // composite primary key
.HasKey(p => new { p.OneId, p.TwoId });
modelBuilder.Entity<OneTwo>()
.HasRequired(a => a.One)
.WithMany(c => c.OneTwos)
.HasForeignKey(fk => fk.OneId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<OneTwo>()
.HasRequired(a => a.Two)
.WithMany(c => c.OneTwos)
.HasForeignKey(fk => fk.TwoId)
.WillCascadeOnDelete(false);
// TODO: handle orphans when last asociation is deleted
}
An alternative strategy is to configure EF relationships via EntityTypeConfiguration<>. The following many-to-many relationship implementation demonstrates that approach:
City.cs
public partial class City
{
public virtual int Id { get; set; }
private ICollection<CountyCity> _countiesCities;
public virtual ICollection<CountyCity> CountiesCities
{
get { return _countiesCities ?? (_countiesCities = new List<CountyCity>()); }
set { _countiesCities = value; }
}
}
County.cs
public partial class County
{
public virtual int Id { get; set; }
private ICollection<CountyCity> _countiesCities;
public virtual ICollection<CountyCity> CountiesCities
{
get { return _countiesCities ?? (_countiesCities = new List<CountyCity>()); }
set { _countiesCities = value; }
}
}
CountyCity.cs
public partial class CountyCity
{
public virtual int CountyId { get; set; }
public virtual int CityId { get; set; }
public virtual County County { get; set; }
public virtual City City { get; set; }
}
CountyCityConfiguration.cs (EF 6 implementation)
public class CountyCityConfiguration : IEntityTypeConfiguration<CountyCity>
{
public void Map(EntityTypeBuilder<CountyCity> builder)
{
// Table and Schema Name declarations are optional
//ToTable("CountyCity", "dbo");
// composite primary key
builder.HasKey(p => new { p.CountyId, p.CityId });
builder.HasOne(pt => pt.County)
.WithMany(p => p.CountiesCities)
.HasForeignKey(pt => pt.CountyId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(pt => pt.City)
.WithMany(t => t.CountiesCities)
.HasForeignKey(pt => pt.CityId)
.OnDelete(DeleteBehavior.Restrict);
// TODO: handle orphans when last association is deleted
}
}
Entity Framework 6 Implementations:
You may configure the composite key and relationships using EntityTypeConfiguration<> as the previous code demonstrates.
Entity Framework Core Implementations:
EntityTypeConfiguration<> has not yet been migrated. However, it is on the roadmap for the next release.
In the meantime, you can employ the temporary pattern suggested by the EF team, or one of the patterns discussed this rather lengthy StackOverflow post discussing entity configuration in Entity Framework 7.
I implemented the pattern posted by Cocowalla in the lengthy discussion prior to reading the EF Team post. The source code for my workaround is available in this GitHub repository.
IEntityTypeConfiguration.cs
namespace Dna.NetCore.Core.DAL.EFCore.Configuration.Temporary.Cocowalla
{
// attribute: https://stackoverflow.com/questions/26957519/ef-7-mapping-entitytypeconfiguration/35373237#35373237
public interface IEntityTypeConfiguration<TEntityType> where TEntityType : class
{
void Map(EntityTypeBuilder<TEntityType> builder);
}
}
Here is my implementation of that pattern:
namespace Dna.NetCore.Core.DAL.EFCore.Configuration.Common
{
public class StateOrProvinceConfiguration : IEntityTypeConfiguration<StateOrProvince>
{
public void Map(EntityTypeBuilder<StateOrProvince> builder)
{
// EF Core
builder.HasOne(p => p.Country).WithMany(p => p.StateOrProvinces).HasForeignKey(s => s.CountryId).OnDelete(DeleteBehavior.Cascade);
builder.HasMany(d => d.Cities).WithOne().OnDelete(DeleteBehavior.Cascade);
builder.HasMany(d => d.Counties).WithOne().OnDelete(DeleteBehavior.Cascade);
}
}
}
I have just started working on entity framework code first approach, I have written two approaches below and both are working fine.
Please let me know what are the core concepts behind both these approaches and what should follow?
Approach 1 : Using EntityTypeConfiguration
public class BlogsMap : EntityTypeConfiguration<Blog>
{
public BlogsMap(string schema)
{
ToTable("BLOG");
HasKey(t => t.BlogId);
Property(t => t.BlogId).HasColumnName("BLOGID");
Property(t => t.Name).HasColumnName("NAME");
Property(t => t.Url).HasColumnName("URL");
}
}
public class BlogContext : DbContext
{
public BlogContext(string name)
: base(name)
{
}
public IDbSet<Blog> BLOG { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BlogMap(string.Empty));
}
}
Approach 2 :
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>();
}
}
Please provide me concept/blogs on entity,since i have just started.
You have several ways to configure your entities. Below I will show three ways,one using DataAnnotations and two using Fluent Api.
The first variant is using DataAnnotations. You can use attributes(DataAnnotations) to configure your entity classes and properties.DataAnnotations attributes overrides default Code First conventions:
[Table(“BLOGS”)]
public class Blog
{
[Key]
[Column(“BLOGID”)]
public int BlogId { get; set; }
[Column(“NAME”)]
public string Name { get; set; }
[Column(“URL”)]
public string Url { get; set; }
public virtual List<Post> Posts { get; set; }
}
[Table(“POSTS”)]
public class Post
{
[Key]
[Column(“POSTID”)]
public int PostId { get; set; }
[Column(“TEXT”)]
public string Text { get; set; }
public int BlogId { get; set; }
[ForeignKey("BlogId")]
public virtual BaseCard Blog { get; set; }
}
Then, in your context class, you don’t need to override the OnModelCreating method, EF will use the attribute to map your entities and relationship (it will create a 1-to-many relationship between blog and post):
public class BlogContext : DbContext
{
public BlogContext(string name)
: base(name)
{
}
public IDbSet<Blog> Blogs { get; set; }
public IDbSet<Post> Posts { get; set; }
}
Configuring with Data Annotations is fairly simple and it may be just what you’re looking for. But Data Annotations only allow you to access a subset of the possible configurations (though much more than you’ve seen so far). The Fluent API , however, gives you access to even more, so you may prefer it for this reason.
With Fluent Api you don’t need to use attributes to map your fields and relationships of your entities classes. There are two ways to use Fluent Api:
1-Mapping the entities (fields and relationships) in the OnModelCreating method in your context (Your second Aproach):
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().ToTable("BLOGS");
modelBuilder.Entity<Blog>().HasKey(t => t.BlogId);
modelBuilder.Entity<Blog>().Property(t => t.BlogId).HasColumnName("BLOGID");
modelBuilder.Entity<Blog>().Property(t => t.Name).HasColumnName("NAME");
modelBuilder.Entity<Blog>().Property(t => t.Url).HasColumnName("URL");
// The same with post
//mapping one-to-many relationship
modelBuilder.Entity<Post>().HasRequired(c => c.Blog)
.WithMany(s => s.Posts)
.HasForeignKey(c => c.BlogId);
}
2-The second variant using Fluent Api is creating mapping classes (Your First Approach). This way, you configure your Entities in classes that inherit of EntityTypeConfiguration<TEntity>:
public class BlogMap : EntityTypeConfiguration<Blog>
{
public BlogMap()
{
ToTable("BLOGS");
HasKey(t => t.BlogId);
Property(t => t.BlogId).HasColumnName("BLOGID");
Property(t => t.Name).HasColumnName("NAME");
Property(t => t.Url).HasColumnName("URL");
}
}
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
ToTable("POSTS");
HasKey(t => t.PostId);
Property(t => t.Text).HasColumnName("TEXT");
//mapping the relationship
HasRequired(c => c.Blog)
.WithMany(s => s.Posts)
.HasForeignKey(c => c.BlogId);
}
}
Then, to include the mappings in your context you need to add them in the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BlogMap());
modelBuilder.Configurations.Add(new PostMap());
}
The best way to add the configurations is at this way:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => type.BaseType != null && type.BaseType.IsGenericType
&& type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
base.OnModelCreating(modelBuilder);
}
This last variant (the first approach) for me is the best due to you don’t have to touch your model classes (adding attributes) to specify what you want and is more flexible if you want to add a new entity or change something.
string nspace = "CompanyAdministration.data.Models";
var q = from t in Assembly.GetExecutingAssembly().GetTypes()
where t.IsClass && t.Namespace == nspace && t.Name[0] != '<' && t.Name.Substring(0, 2) != "BOX" && t.Name != "CAContext"
select t;
foreach (Type t in q)
{
try
{
MethodInfo method = modelBuilder.GetType().GetMethod("Entity");
method = method.MakeGenericMethod(new Type[] { t });
method.Invoke(modelBuilder, null);
}
catch
{
}
}
base.OnModelCreating(modelBuilder);
I am using Visual Studio 2010, C# 4.0 and Entity Framework 5.0. I have been using database first development for many years but am trying to move to code first and am running into problems. Reading and searching does not seem to address the problems
I have simplified my problem as follows - I have two classes - Assessors and Documents.
public class Assessor
{
public int AssessorID { get; set; }
public virtual List<Document> Documents { get; set; }
}
public class Document
{
public int DocumentID { get; set; }
public string DocumentLocation { get; set; }
public string DocumentName { get; set; }
public virtual List<Assessor> Assessors { get; set; }
}
with the context
public class DocumentAssignment : DbContext
{
public DbSet<Assessor> Assessors { get; set; }
public DbSet<Document> Documents { get; set; }
}
An assessor can have many documents and a document can have many assessors (a classic many-to-many relationship).
I am using convention to create the relationship but have also used the fluent API. I have seeded the document table.
My two questions:
ONE - I want to assign documents to assessors - what is the best way to save this to the database?
TWO I have the following method to retrieve documents assigned to an assessor:
public static IEnumerable<MaternalDocument> GetAssignedDocumentList(int UserID, string ConnectionString)
{
using (DocumentAssignment dbContext = new DocumentAssignment(ConnectionString))
{
return returnValue = dbContext.MaternalAssessments
.Where(m => m.AssessorID == UserID)
.Include(m => m.MaternalDocuments)
.Select(m => m.MaternalDocuments)
.ToList();
}
}
but I cannot get this to compile because of mapping issues. What am I doing wrong?
You have to tell the DbContext about how the many-to-many relationship is set up, by overriding OnModelCreating in DocumentAssignment. Replace AssessorDocuments in this code with your relation table name.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Assessor>()
.HasMany(a => a.Documents)
.WithMany(d => d.Assessors)
.Map(m =>
{
m.MapLeftKey("AssessorID");
m.MapRightKey("DocumentID");
m.ToTable("AssessorDocuments");
});
base.OnModelCreating(modelBuilder);
}
To assign a Document to an Assessor (assuming a Document exists with DocumentID of 1 and an Assessor exists with an AssessorID of 1):
using (var context = new DocumentAssignment())
{
var assessor = context.Assessors.Find(1);
var document = context.Documents.Find(1);
assessor.Documents.Add(document);
context.SaveChanges();
}
Your GetAssignedDocumentList method would look something like this:
public static IEnumerable<Document> GetAssignedDocumentList(int UserID)
{
using (var context = new DocumentAssignment())
{
return context.Documents.Where(d => d.Assessors.Any(a => a.AssessorID == UserID));
}
}