Using Two Columns as Discriminator - entity-framework

Is it possible to use two columns as a discriminator. For instance, something like:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Job>()
.HasDiscriminator<string>("Type")
.HasValue<Models.SpecificJob>("attack");
modelBuilder.Entity<Job>()
.HasDiscriminator<string>("Domain")
.HasValue<Models.SpecificJob>("fire_nation");
}
Right now, it seems like it takes only the very last discriminator and ignores the first.

No. A Discriminator indicates the subtype, and EF (and .NET) do not support Multiple Inheritence.
But you can (and should) just use normal properties, one for the "Type" and one for the "Domain".
You can add "Getters" for filtered subsets of your entities. EG:
class Db : DbContext
{
public DbSet<Job> Jobs { get; set; }
public IQueryable<Job> AttackJobs => Jobs.Where(j => j.Type == "attack");
public IQueryable<Job> FireNationJobs => Jobs.Where(j => j.Domain == "fire_nation");
. . .

Related

Is there a way to apply value conversion on backing fields?

Question: Is there a way in Entity Framework Core 3.0 to apply value conversion on backing fields?
Context:
Let's say I have a Blog entity that contains a list of string values representing post ids. I want to avoid the need to have a join entity/table (as described here) so I do a conversion to store the list as a string in the DB. I also want to protect my model from being modified directly through the PostIds property, so I want to use a backing field for that (as described here).
Something like this:
public class Blog
{
public int BlogId { get; set; }
private readonly List<string> _postIds;
public IReadOnlyCollection<string> PostIds => _postIds;
public Blog()
{
_posts = new List<Post>();
}
}
And configuration of the context would look something like that:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configuring the backing field
modelBuilder.Entity<Blog>()
.Metadata
.FindNavigation(nameof(Blog.PostIds))
.SetPropertyAccessMode(PropertyAccessMode.Field);
// Trying to configure the value conversion, but that doesn't work...
modelBuilder.Entity<Blog>()
.Property(e => e.PostIds)
.HasConversion(v => string.Join(',', v),
v => v.Split(','));
}
Any ideas how this configuration could be achieved with the current version of Entity Framework (3.0)?

How to create Mapping rules for custom entities in EF6?

I am new to Entity framework.I got an opportunity to work EF6 using code first approach .When through some concepts in google for creating mapping rules for custom columns i found one way OnModelCreating()
Is there any other way other than this so that we can create tables in db from code first approach.
If there is way..which is better in what context?
Yes, there is other way to map your classes and it better option. At least, I thonk so. You can create mapper for your model which inherits generic EntityTypeConfiguration and add this mapper OnModelCreating. This way your code will stay clean and its much more easier to manage mappings if you have a lot of models.
Model class:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
public int Age { get; set; }
}
Mapper class:
internal class PersonMap
: EntityTypeConfiguration<Person>
{
public PersonMap()
{
// Primary key
this.HasKey(m => m.Id);
// Properties
this.Property(m => m.FullName)
.HasMaxLength(50);
// Table & column mappings
this.ToTable("TABLE_NAME", "SCHEMA_NAME")
this.Property(m => m.Id).HasColumnName("ID");
this.Property(m => m.FullName).HasColumnName("FULL_NAME");
this.Property(m => m.Age).HasColumnName("AGE");
// Relationship mappings
// Map your naviagion properties here if you have any.
}
}
Then you add mapper at OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new PersonMap());
base.OnModelCreating(modelBuilder);
}

Is there a way to have class names be different from your table names?

We are using a database created several years ago, and would like to keep the table names the same.
All of our tables are named like: "tbl_Orders" but we would like the class names for the models / controllers / etc. to be Orders / OrdersController / etc. We are mapping the classes to our tables using Entity Framework.
Sorry if this has been asked before, I tried searching but came up empty handed...
Solution:
After some back and forth with Scott Chamberlain, we came to the conclusion that both answers are correct. I went ahead and marked Masoud's answer as accepted, because that is the route I went. Thank's to everyone who helped (especially Scott).
You can use the Table attribute or the fluent api to map between table names in your database and class names
[Table("tbl_Blogs")]
public class Blog
3rd party edit
Entity framework core offers the same option to map tablenames or columns
map tables names
map column names
The mapping can be done by using attributes
[Table("blogs")]
public class Blog
{
[Column("blog_id")]
public int BlogId { get; set; }
public string Url { get; set; }
}
or by using the fluent api
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("blogs");
modelBuilder.Entity<Blog>()
.Property(b => b.BlogId)
.HasColumnName("blog_id");
}
You can use following code in your DbContext to map all your entities to your tables:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// TableNameConvention
modelBuilder.Types()
.Configure(entity =>
entity.ToTable("tbl_" + entity.ClrType.Name));
base.OnModelCreating(modelBuilder);
}
Working on EF Core 7.0(5.0+) and this one worked for me.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes())
{
// check if current entity type is child of BaseModel
if (mutableEntityType.ClrType.IsAssignableTo(typeof(BaseEntity)))
{
mutableEntityType.SetTableName($"tbl_{mutableEntityType.ClrType.Name.Pluralize()}");
}
}
base.OnModelCreating(modelBuilder);
}

Convention for DatabaseGeneratedOption.None

I would like to manually insert id for all my entities.
Is it possible to create some kind of convention or I have to set HasDatabaseGeneratedOption(DatabaseGeneratedOption.None) (or add DatabaseGenerated attribute) for every entity ?
EDIT
There is a simpler way to do this then using accepted answer:
modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>();
It's possible to create custom conventions in EntityFramework 6 as follows:
Create a convention class
public class ManualIdentityConvention : Convention
{
public ManualIdentityConvention()
{
this.Properties<int>()
.Where(p => p.Name.EndsWith("Id"))
.Configure(p => p.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None));
}
}
Add the convention to your DbContext
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new ManualIdentityConvention());
}
}
EntityFramework 5
As for EntityFramework 5, I believe something similar can be achieved, but not via a convention class:
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<int>()
.Where(x => x.Name.EndsWith("Id"))
.Configure(x => x.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None));
}
}
In either case, neither approach is particularly surgical, but one could conceivably tighten up the convention by being more specific in one's where clause.
Does this help?

Map names for generated relationship tables in EF code first

I'm giving Code first a try and I have the requirement of a prefix of (all) my tables in db.
In my DbContext I have these entities:
public DbSet<Person> People { get; set; }
public DbSet<Department> Departments { get; set; }
I can successfully map table names for my entities by overriding:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().ToTable("w_people");
modelBuilder.Entity<Department>().ToTable("w_departments");
}
However for tables that created that don't directly map to a table I can't figure out to prefix.
In my example people can belong to many departments so a "non-entity" table is created by EF. (I'm a EF noob so these tables probably have a fancy name) So in my db I get three tables:
w_people
w_departments
PersonsDepartments
The PersonsDepartments table is what I'm after. How can I prefix these generated tables or change name/mapping after generation?
TIA
I have solved this:
modelBuilder.Entity<Person>().HasMany(p => p.Departments)
.WithMany(d => d.People)
.Map(mc => mc.ToTable("w_peopledepartments"));