I'm using EF Code First (4.3.1) on a personal ASP.NET MVC 3 project, with a very simple domain model, and I'm almost at the point where EF will generate the DB schema the way I want it to.
The domain model has two classes: Painting and Gallery. Each Painting belongs to a single Gallery, and the Gallery has two virtual properties pointing to Painting: One to indicate which of the painting is it's cover image, and one for which of the paintings is the Slider image displayed on the home page.
The classes are as follow. I've removed some annotations and irrelevant properties to make it readable.
public class Gallery
{
public Gallery()
{
Paintings = new List<Painting>();
}
[ScaffoldColumn(false)]
[Key]
public int GalleryId { get; set; }
public string Name { get; set; }
[ScaffoldColumn(false)]
[Column("LaCover")]
public Painting Cover { get; set; }
[ScaffoldColumn(false)]
[Column("ElSlider")]
public Painting Slider { get; set; }
[ScaffoldColumn(false)]
public virtual List<Painting> Paintings { get; set; }
}
and painting:
public class Painting
{
[ScaffoldColumn(false)]
[Key]
public int PaintingId { get; set; }
public string Name { get; set; }
public int GalleryId { get; set; }
[Column("GalleryId")]
[ForeignKey("GalleryId")]
[InverseProperty("Paintings")]
public virtual Gallery Gallery { get; set; }
public string Filename { get; set; }
}
It generates a correct db schema for both classes and its relationships, the only small issue I have is that I haven't found a way to control the column names it gives to the virtual properties of Cover and Slider in the Gallery table.
It'll name them Cover_PaintingId and Slider_PaintingId.
I tried using the [Column("columnNameHere")] attribute, but that doesn't affect it at all. As in "I typed a certain non related word and it didnt show up in the schema".
I'd like to name it CoverPaintingId, without the underscore.
Any help is greatly appreciated. Thanks
The ForeignKey attribute lets you define the property that will act as your foreign key if you want it to exist within your model:
[ForeignKey("CoverPaintingId")]
public virtual Painting Cover { get; set; }
public int? CoverPaintingId { get; set; }
Note you can put the attribute either on the virtual property on the foreign key - just need to specify the name of the "other one".
However, since you will have two relationships between the same set of entities, you won't be able to do this without disabling Cascading deletes on one or both of them. This can only be done using the Fluent Configuration API.
public class Gallery
{
public int GalleryId { get; set; }
[ForeignKey("CoverPaintingId")]
public virtual Painting Cover { get; set; }
public int? CoverPaintingId { get; set; }
[ForeignKey("SliderPaintingId")]
public virtual Painting Slider { get; set; }
public int? SliderPaintingId { get; set; }
}
public class Painting
{
public int PaintingId { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Gallery> Galleries { get; set; }
public DbSet<Painting> Paintins { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Gallery>().HasOptional(g => g.Cover).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Gallery>().HasOptional(g => g.Slider).WithMany().WillCascadeOnDelete(false);
}
}
If you don't want the foreign key properties to exist in your code model, you can also configure these by using .Map(...) before the .WillCascadeOnDelete(false) part of the API instead of using ForeignKey. I prefer to use Foreign Key, but here's how the code would look if you wanted to do it this way:
public class Gallery
{
public int GalleryId { get; set; }
public virtual Painting Cover { get; set; }
public virtual Painting Slider { get; set; }
}
public class Painting
{
public int PaintingId { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Gallery> Galleries { get; set; }
public DbSet<Painting> Paintins { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Gallery>().HasOptional(g => g.Cover).WithMany().Map(m => m.MapKey("CoverPaintingId")).WillCascadeOnDelete(false);
modelBuilder.Entity<Gallery>().HasOptional(g => g.Slider).WithMany().Map(m => m.MapKey("SliderPaintingId")).WillCascadeOnDelete(false);
}
}
you could try to use [inverseproperty] decoration to achieve this.
Related
I have a .net core 2.0 project using .netcore identity' .
I have a classApplicationUser'(aka Owner) and 3 other classes of media objects Videos, Images, and Audio ("Clips"). So far all is great. where i am hitting a wall is when i try to refactor so that rather than having to hit the DB and say something like:
user=_usermanager.getuserasync(User)
images= _context.Images.Where(i=>i.i.Owner.username== user.username)
I would like to be able to access the same thing by simply saying
user.images
which would hopefully allow me to say things like :'foreach (Audio clip in User.Clips){//do the hokeypokee//}
so i modified my ApplicationUser class to include an ICollection<Type> for each of my media classes. No matter what i do however when i access user.images etc i always get null.(tried virtual nav props as well)
rather than sitting here explaining what i THINK it is (Something im not doing with the getter and setter perhaps?) which would be confusing i figure I just let you guys tell me what direction,I should be going in because I've been down many rabbit holes now trying to figure this out. Entity framework seems in order its just something im not doing ... i cant even figure out (if not in EF somewhere) how the setter is initially defined meaning how does it know what images belong to the user without querying the database to see which images have the fk that much the user... anyway the more i type the more i confuse myself LOL.
User Class:
public class ApplicationUser : IdentityUser, ITraits
{
public int Age { get; set; }
public Ethnicity Ethnicity { get; set; }
public Color EyeColor { get; set; }
public Color HairColor { get; set; }
public int Height { get; set; }
public Race Race { get; set; }
public CuckRole CuckRole { get; set; }
public BiologicalSex BiologicalSex { get; set; }
public Sexuality Sexuality { get; set; }
public Color SkinColor { get; set; }
public int Weight { get; set; }
public DateTime BirthDay { get; set; }
public ICollection<Image> Images { get; set; }
public ICollection<Audio> Clips { get; set; }
public ICollection<Video> Videos { get; set; }
public bool Equals(Traits other) => throw new NotImplementedException();
}
and then my media class (just one all the same just different names)
public class Audio
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
public ApplicationUser Owner { get; set; }
//Constructors//
public Audio()
{
}
public Audio(string path,ApplicationUser owner)
{
Path = path;
Owner = owner;
}
}
EDIT
ok so i tried to implement the suggested code as best i could and thus far this is what i've come up with :
Media Class :
public class Audio
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
public ApplicationUser Owner { get; set; }
public string OwnerId { get; set; }
//Constructors//
public Audio()
{
}
public Audio(string path,ApplicationUser owner)
{
Path = path;
Owner = owner;
}
}
and my ApplicationUser Class now looks like this :
public class ApplicationUser : IdentityUser, ITraits
{
public int Age { get; set; }
public Ethnicity Ethnicity { get; set; }
public Color EyeColor { get; set; }
public Color HairColor { get; set; }
public int Height { get; set; }
public Race Race { get; set; }
public CuckRole CuckRole { get; set; }
public BiologicalSex BiologicalSex { get; set; }
public Sexuality Sexuality { get; set; }
public Color SkinColor { get; set; }
public int Weight { get; set; }
public DateTime BirthDay { get; set; }
public ICollection<Image> Images { get; set; }
public ICollection<Audio> Clips { get; set; }
public ICollection<Video> Videos { get; set; }
public bool Equals(Traits other) => throw new NotImplementedException();
}
and finally my DBContext :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Cucklist.Models.Image> Images { get; set; }
public DbSet<Cucklist.Models.Video> Videos { get; set; }
public DbSet<Cucklist.Models.Audio> Clips { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Image>().ToTable("Image")
.HasOne(Image => Image.Owner)
.WithMany(Owner => Owner.Images)
.HasForeignKey(Image => Image.OwnerId);
builder.Entity<Video>().ToTable("Video")
.HasOne(Video => Video.Owner)
.WithMany(Owner => Owner.Videos)
.HasForeignKey(Video =>Video.OwnerId );
builder.Entity<Audio>().ToTable("Clips")
.HasOne(Audio => Audio.Owner)
.WithMany(Owner => Owner.Clips)
.HasForeignKey(Audio =>Audio.OwnerId );
}
}
sadly i still get a null when accessing user.media (image,video etc)
Hey Guys thanks again for your help (#nilsK) - so the answer ended up being an entity framework "Thing".
Basically lazy loading isn't supported in Entity Framework Core until v2.1 which is in preview now (not sure if i missed making sure to specify it was entity framework core but i guessed that all assumed since it is a .net core project and you have to use Ef Core for a .net core project.) at any rate even with the Microsoft.EntityFrameworkCore updated and installed 2.1.0-previewfinal-2 it still doesn't work .
In order for it to "work" In Other words- not pull back a null when trying to access related entities you must also install the package Microsoft.EntityFramworkCore.Proxies.
Actually this is the easiest way(According to MS) to "enable" lazy loading in ef core; using the proxies. Although two other methods are available that are much more involved to implement
here is the link to the article that finally answered this question once and for all : https://learn.microsoft.com/en-us/ef/core/querying/related-data
so the short and skinny is once you installed the packages you have to (should for above option ) make all call to .UseLazyLoadingProxies() option in your DI container -- go to startup.cs and modify the DBContext options adding .UseLazyLoadingProixes() before the .UseSqlServer(...) and that's it....
now when you instantiate an entity with related entities they will be loaded on first access to the property... so basically It will stop returning a null!!!
otherwise you'd have to use implicit include statements etc... eager loading isnt and option but rather per query declarations...
Summary:
Step 1: Install Microsoft.EntityFrameworkCore preview 2.1.xx
Step 2: Install Microsoft.EntityFrameworkCore.Proxies
Step 3: Modify Services container/Startup.cs (Configure services) DBContext to include the option .UseLasyLoadingProxies
So your old code might look like :
services.AddDbContext(options =>
services.AddDbContext(options =>
options.UseSqlServer(Configuration.GetConnectionString("LocalConnection"))
now it might look like this :
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LocalConnection"))
.UseLazyLoadingProxies());
Step 4 - Modify your DBContext Class to lasy load the related entities (Yes they must all be listed which is probably not optimal but im sure MS will address this)
builder.Entity<Image>().HasOne(u => u.ApplicationUser)
.WithMany(i => i.Images)
.HasForeignKey(u => u.ApplicationUserId)
.HasConstraintName
(
"FK_Image_AspNetUsers_ApplicationUserId"
);
builder.Entity<Video>().HasOne(u => u.ApplicationUser)
.WithMany(p => p.Videos)
.HasForeignKey(u => u.ApplicationUserId)
.HasConstraintName
(
"FK_Video_AspNetUsers_ApplicationUserId"
);
builder.Entity<Audio>().HasOne(u => u.ApplicationUser)
.WithMany(p => p.Audios)
.HasForeignKey(u => u.ApplicationUserId)
.HasConstraintName
(
"FK_Audio_AspNetUsers_ApplicationUserId"
);
you'll notice that im using Identity Core - in the table "AspNetUsers" there is no ApplicationUserId column so you must have the foreign key constraint in place which should already be there if you've modified your class appropriately which i will show below:
Step 5 Make sure you're classes look like this -->
ApplicationUser Class
public class ApplicationUser : IdentityUser, ITraits
{
public int Age { get; set; }
public Ethnicity Ethnicity { get; set; }
public Color EyeColor { get; set; }
public Color HairColor { get; set; }
public int Height { get; set; }
public Race Race { get; set; }
public CuckRole CuckRole { get; set; }
public BiologicalSex BiologicalSex { get; set; }
public Sexuality Sexuality { get; set; }
public Color SkinColor { get; set; }
public int Weight { get; set; }
public DateTime BirthDay { get; set; }
public virtual ICollection<Image> Images { get; set; }<--must be virtual
public virtual ICollection<Video> Videos { get; set; }<--must be virtual
public virtual ICollection<Audio> Audios { get; set; }<--must be virtual
public bool Equals(Traits other)
{
throw new NotImplementedException();
}
}
one of the media classes :
Images:
public class Image
{
//Fields and Properties//
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }<-be virtual
public virtual string ApplicationUserId { get; set; }<--must be virtual
public Image()
{
}
public Image(string path) => Path = path;
}
Step 6
in a view all you need to do is have a ApplicationUser and you can pull the current user using the httpcontext like so :
in your controller preferably although you can do this in the view
ApplicationUser user = await _usermanager.GetUserAsync(HttpContext.User);
then pass the user to the view as
return View(user)
Step 7
in your view make user the model is applicationuser
somewhere in your view try and access the media classes via the user object so
foreach (Audio clip in Model.Clips)
{
<img src="#clip.path" />
}
viola !
**Edit just a helpful read about the topic and proxies etc - ef core 2-1 whats new with lazy loading
In your DbContext-class you can override the OnModelCreating method and configure your relations here.
There are some cleaner ways, but this might be good start.
protected override void OnModelCreationg(DbModelBuilder modelBuilder)
{
// on-to-many relation example
modelBuilder.Entity<Audio>()
.HasOptional(audio => audio.Owner)
.WithMany(owner => owner.Clips)
.HasForeignKey(audio => audio.OwnerId) // this one is missing in your example
.WillCascadeOnDelete(false); // maybe true? depending on what should happen if the user gets deleted
}
You need to tell EF how to manage your relations - you will need a foreign key (Audio.OwnerId).
Another key-word for further investigations might by be ObjectEntityConfiguration.
I'm having quite the issue right now while trying to learn Entity Framework.
Let's say I have this entity:
public class BuildingGroup {
public int ID { get; set; }
public string NameOfManager { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
And also this entity.
public class Architect {
public int ID { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public virtual ICollection<Building> BuildingsBeingWorkedOn { get; set; }
}
These two entities are completely unrelated. Here's the Building entity:
public class Building {
public int ID { get; set; }
public string Address { get; set; }
}
My problem happens when I try to add a building to, say a BuildingGroup. In my domain model, I can modify the equivalent collection of buildings, by adding, modifying or removing buildings. However, when I try to update BuildingGroup through a repository, the buildings will not be updated.
public void Update(BuildingGroup buildingGroup) {
var buildingGroupEntity = _context.BuildingGroups.Single(b => b.ID == buildingGroup.ID);
// This will not map the Building collection
context.Entry(buildingGroupEntity).CurrentValues.SetValues(buildingGroup);
// My attempt at mapping the buildings
buildingGroupEntity.Buildings.Clear();
buildingGroup.Buildings.ToList().ForEach(b => buildingGroupEntity.Buildings.Add(_context.Buildings.Single(x => x.ID == b.ID)));
_context.Entry(buildingGroupEntity).State = EntityState.Modified;
}
This fails if the building were not saved in the database prior to the call to Update(), which is normal since buildings can live independently. It must also be done for every child collection of BuildingGroup (if there were more), and for child collections of these children, well...
I have noticed other people use a foreign key constraint in the child object (here, Building), but I can't really do that since many unrelated entities can point to a building: I'd have a lot of navigation properties.
Is there a graceful way to manage referencing objects that can also live independently from those who hold references to them?
If all the entities have to exist independently, yet have relationships with each other, it's better to use many-to-many relationship.
Change your model classes as follows, the Building should contain a couple of collections for architects and groups.
public class BuildingGroup
{
public int ID { get; set; }
public string NameOfManager { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
public class Architect
{
public int ID { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public virtual ICollection<Building> BuildingsBeingWorkedOn { get; set; }
}
public class Building
{
public int ID { get; set; }
public string Address { get; set; }
public virtual ICollection<Architect> Architects { get; set; }
public virtual ICollection<BuildingGroup> BuildingGroups { get; set; }
}
If you use entity type configuration, you could define the relationship as follows:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Building>().HasMany(it => it.Architects).WithMany(it => it.BuildingsBeingWorkedOn);
modelBuilder.Entity<Building>().HasMany(it => it.BuildingGroups).WithMany(it => it.Buildings);
base.OnModelCreating(modelBuilder);
}
}
Hi I have problem with EF Core insert entity. The problem is that I need to insert new entity with relation to another one which is already existing. I have created the relations with fluent API. I have done this for two times. First I am creating car and adding the last edited by field with Identity user and all works but when I am trying to do the same with another entity it crashes down with
My fluent APi code which works good:
builder.Entity<Car>()
.HasOne(x => x.Owner)
.WithMany(x => x.OwnerCars)
.HasForeignKey(x => x.OwnerId);
Here is car entity:
public class Car : CarBase
{
[Key]
public int CarId { get; set; }
public bool IsTrailer { get; set; }
public virtual TrailerType TrailerType { get; set; }
public virtual int? TrailerTypeId { get; set; }
public virtual ApplicationUser Owner { get; set; }
public virtual string OwnerId { get; set; }
}
and here is Application user entity
public class ApplicationUser : IdentityUser
{
[MaxLength(100)]
public string Address { get; set; }
public DateTime CreatedDateTime { get; set; }
public DateTime LastEditationDateTime { get; set; }
public virtual ApplicationUser LastEditedBy { get; set; }
public bool IsDeleted { get; set; }
public virtual DateTime DeletedDateTime { get; set; }
public ICollection<DriverLicenseApplicationUser> DriverLicenses { get; set; }
public ICollection<RideApplicationUser> Rides { get; set; }
public ICollection<Car> OwnerCars { get; set; }
public ICollection<Car> EditedCars { get; set; }
public ICollection<Trailer> EditedTrailers { get; set; }
public ICollection<Customer> EditedCustomers { get; set; }
}
To add this entity I only call this function and all works.
public Car CreateCar(Car car)
{
_context.Cars.Add(car);
return car;
}
But when I want to save this way this another entity type it shows an error. All steps are same so I do not understand this. Here I am adding the code I use to do that.
builder.Entity<Trailer>()
.HasOne(x => x.TrailerType)
.WithMany(x => x.Trailers)
.HasForeignKey(x => x.TrailerTypeId);
Here is Trailer:
public class Trailer : CarBase
{
[Key]
public int TrailerId { get; set; }
//[Required]
public virtual TrailerType TrailerType { get; set; }
public virtual int TrailerTypeId { get; set; }
}
and here is traylerTyper:
public class TrailerType:Trackable
{
//[Key]
public int TrailerTypeId { get; set; }
[MaxLength(100)]
[Required]
public string Type { get; set; }
public string Note { get; set; }
public ICollection<Car> TrailerTypeCars { get; set; }
public ICollection<Trailer> Trailers{ get; set; }
}
and the method is the same as the one already mentioned
public Trailer CreateTrailer(Trailer trailer)
{
trailer.TrailerTypeId = trailer.TrailerType.TrailerTypeId;
//_context.Attach(trailer.TrailerType);
var result = _context.Trailers.Add(trailer);
return result.Entity;
}
When I uncomment the attach it works but I think that I dont have to attach this because I have got the relation based on IDs and the example mentioned first works great. It gives me no sense. So if anyone could give me advice it would be awsome.
Here is the error I am getting:
Cannot insert explicit value for identity column in table 'TrailerTypes' when IDENTITY_INSERT is set to OFF.
It looks like the EF doesnt know that the traylertype entity already exists and is trying to insert the same entity again and the app crashes because it already exists and I am not allowing to insert IDs directly. As I said I have absolutely no idea why is this happening.
The problem is Lazy loading. Propetry from ViewModel is not completly same as property in Database and EF tracks whole graph of property in object and doesn´t recognize that it is the same object. The solution is to work only with IDs instead with whole objects.
This is my first time here so sorry if I did not put the doubt by default. In my project I have two models class doubts and a class of works, I want to create a classification for both using a superclass, like the design pattern strategy. But I have a problem, the project state is advanced and I have used the doubt and works classes many times. Already tried in many ways, but either the error in the update-database or the error in what I had done before.
Does anyone know one way that I can implement a generic class classification for the doubts and works classes without many changes of what I had already done?
My code is below
public abstract class Classificable
{
[Key]
public int id { get; set; }
public virtual Classification classication { get; set; }
}
public class Doubt : Classificable
{
public int doubtID { get; set; }
public string question { get; set; }
public string content { get; set; }
public virtual Student student { get; set; }
public virtual Course course { get; set; }
public virtual Work work { get; set; }
public virtual ICollection<Answer> answers { get; set; }
}
public class Work : Classificable
{
public int workID { get; set; }
public string name { get; set; }
public string nameWork { get; set; }
public string filePath { get; set; }
public virtual Student student { get; set; }
public virtual Course course { get; set; }
public virtual ICollection<Doubt> doubts { get; set; }
}
public class DB_DIS : DbContext
{
public DB_DIS()
: base("name=DB_DIS")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Doubt>().ToTable("Doubts");
modelBuilder.Entity<Work>().ToTable("Works");
}
public virtual DbSet<Doubt> Doubts { get; set; }
public virtual DbSet<Work> Works { get; set; }
}`
What is the property or method in Classificable that you are trying to apply to Doubt and Work?
Have you written your data structure in such a way that is contains an id field AND workID feild?
In my experience, if you are trying to share properties between EF Classes you are better off sharing common fields. I've used it in the past for base classes of AuditableBase as follows:
public class AuditableBase
{
public string UpdateUserId { get; set; }
public DateTime UpdateDate { get; set; }
}
Then all my classes that I want to "Audit" will have a UpdateUserId and UpdateDate, and I can do some pre-save processing to set those based on the type of AuditableBase.
I have a relatively complex relationship I need to set up between a User object and a lot of lookup tables. The user object is your run of the mill user model:
public class Youth : IAuditInfo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid YouthGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public string ImageName { get; set; }
[ForeignKey("FkYouthId")]
public ICollection<User> Parents { get; set; }
public CubPack Pack { get; set; }
public virtual ICollection<RequirementsLog> RequirementsLogs { get; set; }
public Youth()
{
Parents = new List<User>();
}
}
The lookup tables is where it gets complex and I can't figure out the path of least complexity in binding them together. For the lookups it is a series of tables starting with one 'master' table, that rolls down hierarchically to requirements and sub requirements, like this:
Master:
public class BearTrail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<BearTrailRequiredBadge> BearTrailRequiredBadges { get; set; }
public virtual ICollection<BearTrailElectiveBadge> BearTrailElectivedBadges { get; set; }
}
Required Badges:
public class BearTrailRequiredBadge
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string Description { get; set; }
public virtual ICollection<BearTrailRequiredBadgeSubRequirement> BearTrailRequiredBadgeSubRequirements { get; set; }
}
Required Badge sub requirement:
public class BearTrailRequiredBadgeSubRequirement
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Number { get; set; }
public string Text { get; set; }
public bool Required { get; set; }
}
This is one set of the lookups, there are about four nested classes like this, and some one off tables as well. Total lookup tables is about 16, give or take.
I was initially thinking if using my RequirementLog model to bind it:
public class RequirementsLog
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Youth> Youth { get; set; }
public BearTrail BearTrailRequirements { get; set; }
public TigerTrail TigerTrailRequirements { get; set; }
public WolfTrail WolfTrailRequirements { get; set; }
public WebelosTrail WebelosTrailRequirements { get; set; }
public WebelosArrowOfLight WebelosArrowOfLightRequirements { get; set; }
}
So there is a many to many between RequirementsLog and Youth. The table created out of RequirementsLog has one PK column (ID), and FK columns for each property. The many to many table created out of this (RequirementsLogYouths) has two PKs (RequirementsLogId, and YouthId).
Am I going about this the right way? The end goal is to have the 16 or so tables server as just lists of various requirements, and have another table(s) to track a particular youths progress through the requirements. I have a hard time visualizes some of this DBA stuff, so any input would be greatly appreciated.
In most cases, a requirements "log" be in a one (people) to many (the log).
Unless... One logged item is for many kids...
If so, the you need a third table, that maps many people to multiple logged events. That is, if this is truly a many to many. In general, that situation almost always begs for a third, intermediate mapping table. Read up a bit on many to many designs, and you'll quickly see it, and how simple it is.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Entity1>()
.HasMany(b => b.Entities2)
.WithMany(p => p.Entities1)
.Map(m =>
{
m.ToTable("Entitie1Entity2");
m.MapLeftKey("Entity1Id");
m.MapRightKey("Entity2Id");
});
}