Here im trying to get data from database using code-first but if I have a tabel Like Country, like this:
public class Country
{
public int id { get; set; }
public string Name { get; set; }
}
contextdb
public class ContextDb: DbContext
{
public ContextDb() { }
public DbSet<Country> Country { get; set; }
...
}
when I implements as Countrys its throwing an error:
Countries not have dbo
Please try to make it explicit which specific table you want for this particular type
public class ContextDb : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Country>().ToTable("Country");
}
public ContextDb() { }
public DbSet<Country> Country { get; set; }
}
As an alternative, you can turn off the pluralizing convention
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
Another option, as mentioned in Stefan's answer, is to have an attribute over your model class (which makes the class "not-that-POCO-anymore" but technically is perfectly valid).
Usually we use plurals in the DbContext like this:
public DbSet<Country> Countries { get; set; }
EF uses an auto plural convention, so your entity
public class Country
will translate to a datatable [dbo].[Countries] in your database.
If you want to explicitly override the name of the table in the database you can use the following attribute.
[Table("YourNameHere")]
public class Country
{
public int id { get; set; }
public string Name { get; set; }
}
I think there is a mixup in this logic somewhere in your code.
Alternativly you can also put your table in a differnt schema if you like:
[Table("YourNameHere", Schema = "YourSchemaNameHere")]
public class Country
{
public int id { get; set; }
public string Name { get; set; }
}
Beware with renaming the schema though, I have experienced that some migrations will break because to database index names are not always handled correctly.
note
See #Wiktor Zychla 's solution to disable the plural conventions on the whole context.
Related
I am trying to build a datawarehouse (DWH), using the code-first approach (star-schema):
Fact-/dimension classes:
[Table("FactBase")]
public class FactBase
{
[Key]
public Guid Id { get; set; }
[ForeignKey("DimTest1")]
public string DimDigitalesBuchISBN { get; set; }
public virtual DimTest1 DimTest1 { get; set; }
}
[Table("DimTest1")]
public class DimTest1
{
[Key]
public string ISBN { get; set; }
public string Bla { get; set; }
}
Context:
public class XDBContextDWH : DbContext
{
public DbSet<FactBase> FactBase { get; set; }
public DbSet<DimTest1> DimTest1 { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(new string("connection string"));
}
}
After using migration the schema looks like this:
Based on that star schema, shouldn't be the relationship (here: SQL) like that?
When you specify the attribute [ForeignKey("DimTest1")], you're telling EF to use DimTest1 as the navigation property of the FactBase class, not pointing to the DimTest1 class.
But since that property does not exist, it does not create the relationship.
Change your class to:
[Table("FactBase")]
public class FactBase
{
[Key]
public Guid Id { get; set; }
[ForeignKey("DimTest1")]
public string DimDigitalesBuchISBN { get; set; }
public virtual DimTest1 DimTest1 { get; set; } // navigation property
}
This should make it work as intended.
As you imply is your question, the star schema Fact table should use a composite key made up of the foreign keys it's referencing.
So I would say there are a couple issues with your situation that should be addressed.
First, a fact table probably shouldn't have a a column called Id, though it's not really going to hurt anything, it probably wouldn't ever be used to query by, so you are just adding extra data taking up disk space.
Second, and probably the answer you are looking for is, if you want a composite primary key on your fact table, you need to specify that in the Database Context.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<FactBase>()
.HasKey(x => new { x.Id, x.DimDigitalesBuchISBN });
}
As I mentioned, you probably don't want to include the Fact.Id column in your PK but instead you would refer to more than one dimension like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<FactBase>()
.HasKey(x => new { x.Dim1Id, x.Dim2Id, x.Dim3Id});
}
Where Dim1Id, Dim2Id and Dim3Id are the primary keys of your dimensions.
I should also mention that you need to remove the [Key] attribute from the Id field of your FactBase class.
refer to: https://learn.microsoft.com/en-us/ef/core/modeling/keys?tabs=data-annotations
I have a need for a project to allow the user to setup database info and table names in the config file. I want to use ADO.NET Entity Model to use the LINQ and just stay away from SQL the most I can to make it easier on myself. Is there a way to dynamically assign what table a Class needs to access for the modal?
For example:
This is what it looks like normally
[Table("database.table")]
public partial class table
{
[Key]
[Column(TypeName = "usmallint")]
public int ID { get; set; }
[Required]
[StringLength(128)]
public string Instance { get; set; }
[Required]
[StringLength(60)]
public string Name { get; set; }
}
I want to be able to dynamically set the TableAttribute so the model knows what table to access for the class.
[Table(Config.DBName + Config.tableName)]
public partial class table
{
[Key]
[Column(TypeName = "usmallint")]
public int ID { get; set; }
[Required]
[StringLength(128)]
public string Instance { get; set; }
[Required]
[StringLength(60)]
public string Name { get; set; }
}
Any help or letting me know that it is not possible would be appreciated.
I've not tested this, but I think you can do this via implementing custom conventions - if you're using EF6 at least.
First, you need to create a custom Convention:
public class CustomTableNameConvention : IStoreModelConvention<EntitySet>
{
private readonly string _tablePrefix;
public CustomTableNameConvention(string tablePrefix)
{
_tablePrefix = tablePrefix;
}
public void Apply(EntitySet item, DbModel model)
{
//change table name.
item.Table = $"{_tablePrefix}" + item.Table;
}
}
Next, you need to add this convention in the OnModelCreating method of your Context:
public class MyContext : DbContext
{
public MyContext(string connectionString) : base(connectionstring)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//get the dynamic table prefix...
var myAppPrefix = "user1";
modelBuilder.Conventions.Add(new CustomTableNameConvention(myAppPrefix));
base.OnModelCreating(modelBuilder);
}
public DbSet<SomeModel> { get; set; }
...
}
... Then, whenever the model in this application instance starts up, it should run through the above when deciding what the table name(s) should be.
Just replace the myAppPrefix = ... code with a call to an appropriate service to get the prefix for this instance.
the obvious caveat with this is that you cannot use a value for the prefix which is returned from the database (at least, not via this Context), as the Context isn't yet initialised.. so you'd have to either store it in settings or pass it in some other way.
Hope this helps.
This is an extension of this question which from what I can tell now works in EF6. However, it seems as though there's a problem when you've got subclasses which have both shared and unshared properties.
Let's say this is my model setup:
public abstract class Document
{
public int Id { get; set; }
public string NameOnDocument { get; set; }
}
public class BirthCertificate : Document
{
public string RegistrationNumber { get; set; }
}
public class Licence : Document
{
public string LicenceNumber { get; set; }
}
In the database, I want BirthCertificate.RegistrationNumber and Licence.LicenceNumber to share the same column, Number. As such, I'm setting up my model like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Document - base class
modelBuilder.Entity<Document>().HasKey(d => d.Id);
modelBuilder.Entity<Document>()
.Property(d => d.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Document>()
.Property(d => d.NameOnDocument)
.HasColumnName("Name");
// Birth certificate
modelBuilder.Entity<Document>().Map<BirthCertificate>(map =>
map.Property(c => c.RegistrationNumber).HasColumnName("Number"));
// Licence
modelBuilder.Entity<Document>().Map<Licence>(map =>
map.Property(l => l.LicenceNumber).HasColumnName("Number"));
}
When I generate the database, it all looks and works as expected:
Now for the issue at hand. Let's say the Licence entity also needs to record an expiry date; so I add that in as follows:
public class Licence : Document
{
public string LicenceNumber { get; set; }
public DateTime ExpiryDate { get; set; }
}
Now when I regenerate the database, it looks like this:
What's more, if I try to insert a licence and a birth certificate, I get the following exception:
An unhandled exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll
Additional information: A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
I can understand why that exception is raised - because the database is useless.
What have I missed?
Ok, so it turns out that the problem is simple to solve, and yet so far as I can tell is not documented anywhere. So hopefully this will help someone with the same problem.
Seemingly, the key is that you have to map every property on the derived entity:
modelBuilder.Entity<Document>().Map<Licence>(map =>
{
map.Property(l => l.LicenceNumber).HasColumnName("Number");
map.Property(l => l.ExpiryDate).HasColumnName("ExpiryDate");
});
Now my database generates as I would expect and all is right with the world.
I'm not sure you are gaining much by putting 2 columns into 1 in the database. I don't think you'll save much space and you can't interrogate the Number of a base Document, even though both documents will have one. Have you considered adding the Number field to the base and using data annotations on overrides - something like this?
public abstract class Document
{
public int Id { get; set; }
public string NameOnDocument { get; set; }
public virtual string Number
}
public class BirthCertificate : Document
{
[Display(Name="Registration Number"]
[Required]
public override string Number { get; set; }
}
public class Licence : Document
{
[Display(Name="Licence Number"]
[Required]
public override string Number { get; set; }
}
EDIT And if all documents aren't numbered:
public abstract class Document
{
public int Id { get; set; }
public string NameOnDocument { get; set; }
}
public abstract class NumberedDocument : Document
{
public virtual string Number
}
public class BirthCertificate : NumberedDocument
{
[Display(Name="Registration Number"]
[Required]
public override string Number { get; set; }
}
Courses have many prerequisites, and simultaneously a particular course can be a prerequisite for many courses. I've tried to establish the many-to-many relationship (in OnModelBCreating) using EF code-first with the following:
modelBuilder.Entity<Course>()
.HasMany(e => e.Prerequisites)
.WithMany(e => e.Postrequisites)
.Map(m => m.ToTable("CourseRequisiteMappings")
.MapLeftKey("CourseId").MapRightKey("CourseId")); // EDIT: THIS LINE IS THE PROBLEM. SEE MARKED ANSWER AND MY COMMENT ON IT.
Also, here is the Course class:
public class Course
{
public int CourseId { get; set; }
public string Name { get; set; }
public string InstitutionCode { get; set; }
public string Description { get; set; }
public bool IsElective { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<Module> Modules { get; set; }
public virtual ICollection<Course> Prerequisites { get; set; }
public virtual ICollection<Course> Postrequisites { get; set; }
}
When I implemented this and went to update the database, it gave me the following errors:
CourseId: Name: Each property name in a type must be unique. Property
name 'CourseId' is already defined.
ModuleId: Name: Each property name in a type must be unique. Property
name 'ModuleId' is already defined.
CourseCourse: EntityType: EntitySet 'CourseCourse' is based on type
'CourseCourse' that has no keys defined.
ModuleModule: EntityType: EntitySet 'ModuleModule' is based on type
'ModuleModule' that has no keys defined.
I could not find an example of doing this which leads me to believe one of the following three are true:
There's a different way of accomplishing this that I don't see
I'm on the right track but overlooking something due to my lack of knowledge with EF
I'm the first one to try and EF doesn't support this (very unlikely)
First, does anyone know how I can set up this relationship, i.e., what do these errors mean (responding to #2)? For bonus points, is there another way of doing this that might be better or worse (kinda #1)? Thanks in advance.
Your mapping is nearly correct. But you have to understand that under the hood the Entity Framework wil create a so callled junction table that stores the many to many relationship.
This junction table wil just have two fields, containing the foreign keys which together make up the primary key. Obviously these foreign keys cannot have the same name.EF is smart enough to all figure it out by itself and no maping is necessary. Below a working example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
namespace ManyToManyUnderTheHoodSpike
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<CourseContext>());
using (CourseContext context=new CourseContext())
{
context.Courses.Add(new Course("Top of the bill")
{
PrerequisiteCourses = new List<Course>()
{
new Course("My two cents"),
new Course("Counting to two")
}
});
context.SaveChanges();
}
}
}
public class CourseContext : DbContext
{
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class Course
{
public Course() { }
public Course(string name)
{
Name = name;
}
public string Name {get;set;}
public int CourseId{get;set;}
public ICollection<Course> PrerequisiteCourses{get;set;}
public ICollection<Course> FollowUpCourses{get;set;}
}
}
If you run this code you get a database with two tables: Courses and CourseCourses with as the only fields Course_Id and Course_Id1.
But that is not very readable, so let's make the mapping to make it more readable:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Course>().HasMany(course => course.PrerequisiteCourses)
.WithMany(course => course.FollowUpCourses)
.Map(data => data.ToTable("Prerequisites")
.MapLeftKey("FollowUpId")
.MapRightKey("PrerequisiteId"));
}
Presto!
I would model like this. I know you wanted only 1 table. But Ef will create the many to many table if you dont. Not sure what you didnt get right without testing. So anyway, here is another option.
public class Course
{
public int CourseId { get; set; }
public string Name { get; set; }
public string InstitutionCode { get; set; }
public string Description { get; set; }
public bool IsElective { get; set; }
//nav elements
public virtual ICollection<Instructor> Instructors { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<Module> Modules { get; set; }
public virtual ICollection<PreReqCourse> Prerequisites { get; set; }
// You can Find follow on courses, by accessing PreReqCourse table, but if you felt this navigation offered enough value, create a post req table too. Using same approach.
// public virtual ICollection<Course> Postrequisites { get; set; }
}
public class PreReqCourse
{
public virtual int Id {get; set;}
public virtual int CourseId { get; set; }
public virtual Course PreReqForCourse { get; set; } //Nav prop
}
modelBuilder.Entity<Course>()
.HasMany(e => e.Prerequisites)
.WithMany();
// Leave WithMany empty. You can define in PreReqCourse Table model, you dont need to model from both directions.
modelBuilder.Entity<PreReqCourse>()
.HasRequired(e => e.PreReqForCourse)
.HasForeignKey(f => f.CourseId)
.WithMany(p=>p.PreRequisites);
I was curious if it is possible to map an intermediate table through a containing object.
public class Subscriber : IEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
private ChannelList _subscribedList { get; set; }
public int NumSubscribedChannels { get { return _subscribedList.Count(); } }
}
public class HelpChannel : IEntity
{
[Key]
public int Id { get; set; }
public string name { get; set; }
public string category { get; set; }
public int group { get; set; }
}
I need to have a subscriber table, channel table and an intermediate table to link a subscriber to his/her channels.
Is it possible to map the list that is within the ChannelList object to the Subscriber Model?
I figured that's probably not possible and that I would need to just have a private List for EF to map. But I wasn't sure if EF will do that for private variables. Will it?
I'm hoping that is does because if it has to be public to maintain the encapsulation.
You can map private properties in EF code-first. Here is a nice description how to do it. In your case it is about the mapping of Subscriber._subscribedList. What you can't do is this (in the context's override of OnModelCreating):
modelBuilder.Entity<Subscriber>().HasMany(x => x._subscribedList);
It won't compile, because _subscribedList is private.
What you can do is create a nested mapping class in Subscriber:
public class Subscriber : IEntity
{
...
private ICollection<HelpChannel> _subscribedList { get; set; } // ICollection!
public class SubscriberMapper : EntityTypeConfiguration<Subscriber>
{
public SubscriberMapper()
{
HasMany(s => s._subscribedList);
}
}
}
and in OnModelCreating:
modelBuilder.Configurations.Add(new Subscriber.SubscriberMapping());
You may want to make _subscribedList protected virtual, to allow lazy loading. But it is even possible to do eager loading with Include:
context.Subscribers.Include("_subscribedList");