When I create a model class and set id in DbContext, Entity Framework reads and writes this model data to database.
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
}
The primitive types are programmed by entity framework how to read and write. But I need a custom type named geometry. This type may be 2D or 3D geoemtry.
public class Point{
public double X { get; set; }
public double Y { get; set; }
public int Dimension { get; set; }
}
So when I set this POint type in MyModel the entity framework does not know how to read and write it. It will be a column in MyModel table. Point data willl be serialize binary or another method.
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
public Point Geometry { get; set; }
}
For example table data will be like this:
Id Name Geometry
1 P1 POINT(10,13)
1 P2 POINT(20,30)
But I should override or inject somethings in Entity Framework to how to read and write my custom type. Is this possible?
Related
Scenerio:
public class Department
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public string Description { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class TestItem
{
public int TestItemId { get; set; }
public string TestItemName { get; set; }
public Department Department { get; set; }
public int DepartmentId { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class Patient
{
public int PatientId { get; set; }
public string PatientName { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
the problem is that, every time I create a table I have to add those two columns repeatedly.
But I want like this-
public class EntryLog
{
public int EntryLogId { get; set; }
public DateTime CreatedOn {get; set; }
public string CreatedBy {get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public string Description { get; set; }
public EntryLog EntryLog { get; set; }
public int EntryLogId { get; set; }
}
and so on...
class A { .. }
class B { .. }
But its creating problem [showing conflicts error with other table's foreign key] while creating a row for a Department or a Patient.
In EF core, there is Table Per Hierarchy (TPH) but in that case every table will be merged into a single table. But that doesn't give me any solution.
looking forward to expert's suggestion...
The bottom line is: use EntryLog as a base type and don't tell EF about it. It's easy enough to keep EF-core oblivious of the base type: only register the derived types. Doing so, EF-core will map your subtypes to their own tables, just as if they didn't have a common type.
Now EntryLog will no longer need an Id, and it should be abstract:
public abstract class EntryLog
{
public DateTime CreatedOnUtc { get; set; }
public string CreatedBy { get; set; }
}
Whether this is enough depends on your specific requirements. There are several possibilities.
1. No additional configuration
If you're happy with the default conventions EF will apply to the common properties, your done. CreatedOnUtc will be mapped to a DateTime2 column (in Sql Server) and CreatedBy to an nvarchar(max) column in each table for an EntryLog entity.
However, if you do need custom configurations --for example if you want to map CreatedBy to an nvarchar(50) column-- additional mapping instructions should be applied. And of course you still want to do the mapping of the common properties only once --which would also happen if you did map the base type in a TPH scheme. How to do that?
2. Data annotations in the base type
The easiest option is to add data annotations:
public abstract class EntryLog
{
public DateTime CreatedOnUtc { get; set; }
[MaxLength(50)]
public string CreatedBy { get; set; }
}
And that's all.
But there are dev teams that don't want to use data annotations for mapping instructions. Also, EF's fluent mappings offer more options than data annotations do. If data annotations don't fit the bill for whatever reason, fluent configurations must be applied. But still, you only want to configure the common properties only once. A viable way to achieve that is to use IEntityTypeConfigurations for each EntryLog and let each concrete configuration derive from a base class. This offers two more options.
3. The base class contains regular properties
Option 4 will make clear why I talk about "regular properties" here. This is what it looks like:
abstract class EntryLogConfiguration
{
public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
where TEntity : EntryLog
{
// Just an example of how to configure a base property.
builder.Property(e => e.CreatedBy).HasMaxLength(50);
}
}
class DepartmentConfiguration : EntryLogConfiguration,
IEntityTypeConfiguration<Department>
{
public void Configure(EntityTypeBuilder<Department> builder)
{
builder.Property(p => p.DepartmentName).HasMaxLength(100);
ConfigureBase(builder);
}
}
And in the context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new DepartmentConfiguration());
}
4. Using shadow properties
Shadow properties is a new feature of EF-core.
Shadow properties are properties that are not defined in your .NET entity class but are defined for that entity type in the EF Core model. The value and state of these properties is maintained purely in the Change Tracker.
Let's suppose you want to have CreatedBy as a class property (because you want to show it in a UI) but only need CreatedOnUtc as a property that's set in the background and that shouldn't be exposed. Now EntryLog will look like this:
public abstract class EntryLog
{
public string CreatedBy { get; set; }
}
So the property CreatedOnUtc is gone. It has been moved to the base configuration as shadow property:
abstract class EntryLogConfiguration
{
public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
where TEntity : EntryLog
{
builder.Property(e => e.CreatedBy).HasMaxLength(50);
builder.Property<DateTime>("CreatedOnUtc");
}
}
Now you can't set CreatedOnUtc directly, only through EF's change tracker. The best place to do that is in an override of SaveChanges in the context:
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<EntryLog>())
{
entry.Property<DateTime>("UpdatedOnUtc").CurrentValue = DateTime.UtcNow;
}
return base.SaveChanges();
}
Of course, if UpdatedOnUtc was a regular property, this override would also come in handy, but you could just do
entry.Entity.CreatedOnUtc = DateTime.UtcNow;
I hope this will give you enough food for thought to figure out which option suits you best.
Quite new with LINQ. I am wondering how I would be able to achieve this.
I have the following table classes defined:
public partial class Cars
{
public long ID { get; set; }
public string CarName { get; set; }
public long CarModelID { get; set; }
public virtual CarModel CarModel { get; set; }
}
public partial class CarModel
{
public long ID { get; set; }
public string ModelName { get; set; }
public long StockID { get; set; }
}
public partial class Stock
{
public long ID { get; set; }
public string StockName { get; set; }
}
There's also a defined extension for the class Cars (Cars.extension.cs):
public partial class Cars
{
public List<Stock> StockList { get; set; }
}
I am trying to get all the Cars, CarModel and (List of) Stocks via the following query:
var query = (from cars in Context.Cars.Include("CarModel").Include("StockList")
select cars).FirstOrDefault();
It is giving me an error:
"A Specified Include Path is not Valid. The Entity Type Cars does not declare a Navigation Property with the name 'StockList'"
How would I be constructing my LINQ query such it would include possibly the list of Stocks based on a CarModel based off Cars?
The Include method is adhering to FluentAPI principles, that means further Include() calls are still in the context of the parent entity (Cars) and not in the previously included CarModel.
What you need is:
Cars.Include("CarModel.StockList")
Or
Cars.Include(x => x.CarModel.StockList)
I get an Error when I try to map an Entity to a Property of a Baseclass AND to another Property of a derived Class.
Example:
The Error is: Error 1 Running transformation: A member named Car cannot be defined in class MyModel.Racer. It is defined in ancestor class MyModel.Person.
Is there a mistake in my DB-Model?
The FavouriteCarID should not appear on the inheriting type's entity - it should only be mapped to a property on the base type.
Try to remove FavouriteCarID from person or RaceCarID from Racer..
Because CarID is already mapped with person .so we cannot refer that again in Racer table...
Its like u r trying to create a duplicate class for person...so it leads to error dude...
I hope u can correct the issue... Remove the mapping between Car and Racer tables. It will work
The Problem here is the Navigationproperties. I just had to rename the "Car"-Propertie in the Baseclass to "FavouriteCar" and in the derived class "Car" to RaceCar. Works Nice!
Please find the table structure.... Hope u r looking for this. This way u can create Db structure know??
public class car
{
public int CarID { get; set; }
}
public class Person
{
public int personid { get; set; }
// List<Favourite> .. we can do by querying dude only.
}
public class FavouriteCarList : Person, car
{
public int ID { get; set; }//pk
public int personid { get; set; }//FK
public int FavoriteCarID { get; set; }//FK from Car
}
public class Racer : car,person
{
public int ID { get; set; }//pk
public int personid { get; set; } //Fk
public int RaceCarID { get; set; }//FK from Car
}
I have these three models:
public class Equipment
{
public int ID { get; set; }
public string title { get; set; }
}
[Table("Vessels")]
public class Vessel:Equipment
{
public string Size { get; set; }
}
[Table("Tubes")]
public class Tube : Equipment
{
public string Pressure{ get; set; }
}
I want to show a list of Equipments with 2 columns title and type.
for example:
Title Type
------ -------
101-1 vessel
101-2 vessel
102-3 tube
I don't know how to make a discriminator column in Equipments to show the type of each equipments.
EDITED
If I have a discriminator in Equipment entity like:
public class Equipment
{
public int ID { get; set; }
public string title { get; set; }
public string type{ get; set; } //as discriminator
}
I can get the query in controller or repository like this:
var equipments=from e in db.Equipments
select e;
You cannot make discriminator column in terms of EF mapping - TPT inheritance doesn't support it because the discriminator is a subtable. You can try to use something like:
public abstract class Equipment
{
public int ID { get; set; }
public string title { get; set; }
[NotMapped]
public abstract string Type { get; }
}
and override Type property in subtypes to get the correct name. You will not be able to use that property in Linq-to-Entities queries because it is not mapped.
This is my model class.
public class Lead
{
private readonly ObservableCollection<String> m_tags = new ObservableCollection<string>();
public int LeadId { get; set; }
public string Title { get; set; }
public ObservableCollection<String> Tags { get { return m_tags; } }
}
Does Entity Framework offer a way to represent this using either Model-First or Code-First?
EDIT: I'm looking for a way to do this without changing the public API of the model. The fact that there is some sort of Tags table shouldn't be visible to the downstream developer.
Since your model has to be represented in a relational way, you can only use primitive types (that have an equivalent in a SQL DB) or other entities within a entity definition - that means the tags are represented by their own entity. In your case it would be something like this using Code first approach:
public class Lead
{
public int LeadId { get; set; }
public string Name { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public string Name { get; set; }
}
public class SomeContext : DbContext
{
public DbSet<Lead> Leads { get; set; }
public DbSet<Tag> Tags { get; set; }
}
This (by default) will be represented in the database as a table Leads, a table Tags, and a relationship table LeadTags that only contains {LeadId, TagId} pairs.