I'm attempting to map a class which looks like below but I'm getting an exception regarding the Facility.Projects property being an interface.
The property Facility.Projects is of an interface type IProject. If it is a navigation property manually configure the relationship for this property by casting it to a mapped entity type, otherwise ignore the property from the model.
public class Facility : BaseData
{
[ForeignKey("ClientId")]
public Client Owner { get; set; }
public List<IProject> Projects { get; protected set; }
public Facility()
{
Initialize();
}
private void Initialize()
{
Projects = new List<IProject>();
}
}
I've mapped other properties via the OnModelCreating method for the DbContext class that Facility is mapped to but I'm not really seeing anything that seems like the right spot to cast this other than HasColumnType which also appears wrong.
public class FacilityRepository : BaseRepository<Facility>, IFacilityRepository
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<Client>()
.Property(f => f.Id)
.ForSqliteHasColumnName("ClientId");
modelBuilder
.Entity<Project>()
.Property(x => x.Id)
.ForSqliteHasColumnName("ProjectId");
// area where I've tried and failed to find a good spot to do some manual casting
modelBuilder
.Entity<Facility>()
.Property(f => f.Projects)
.HasColumnType<Project>(new PropertyBuilder());
}
}
Can anyone point me in the right direction? I've not seen anything in the documentation that might help? Or should I, by rule, not be doing this at all?
To do a manual mapping of a one-to-many relationship (this example is just exposing the children on the parent, not vice versa), you can set this up as follows, and then it's pretty straightforward where the cast should be:
modelBuilder.Entity<ProjectState>()
.HasMany(h => (ICollection<ProjectRoleState>)h.ProjectRoleStates)
.WithOne()
.HasForeignKey(p=>p.ProjectGuid);
Related
I have the following Entity Framework (v6.1.3) mapping:
public class FileStoreDocumentEntityMapping : EntityTypeConfiguration<FileStoreDocumentEntity>
{
public FileStoreDocumentEntityMapping()
{
Property(x => x.FileStoreDownloadUrl)
.HasColumnName("FileStoreDetailsUrl")
.HasColumnType("varchar")
.HasMaxLength(1000);
Property(x => x.FileStoreVersion)
.HasColumnName("FileStoreVersion")
.HasColumnType("varchar")
.HasMaxLength(100);
}
}
Both columns are of data type = "varchar" in my database and sizes are correct:
However, some of my unit tests are failing with this error:
"System.InvalidOperationException: Sequence contains no matching element"
I read in a related post that you can get the error above if you pass an invalid type to the HasColumnType method but in my case "varchar" should be valid.
Any ideas what could be wrong?
Here is how those properties are defined in my entity:
public virtual string FileStoreVersion
{
get;
set;
}
public virtual string FileStoreDetailsUrl
{
get;
set;
}
Here is an example of one test that fails in the first line:
[TestMethod]
public void Delete_ReturnsSuccess()
{
_context.DistributionListSelectionCriteriaDepartmentEntities.Add(_distributionListSelectionCriteriaDepartmentEntity);
_context in this case is of type MemoryEnterprisePaycorCodeFirstContext:
private void InitializeTestObjects()
{
_context = new MemoryEnterprisePaycorCodeFirstContext();
Which inherits from my public class EnterprisePaycorCodeFirstContext : DbContext
That class is where i'm initializing my entity mappings:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new FileStoreDocumentEntityMapping());
Could this be because I'm using Effort MemoryContext vs a Real DB context?
public class MemoryEnterprisePaycorCodeFirstContext : EnterprisePaycorCodeFirstContext
{
public MemoryEnterprisePaycorCodeFirstContext()
: base(DbConnectionFactory.CreateTransient())
{
Database.CreateIfNotExists();
}
I figured what it was. Turns out that I was using Effort library which does not support some Column Types as pointed out by #Balah. I used his technique to bypass the entity mapping configuration when coming from my Memory Context:
Effort (EF Unit Testing) giving errors
Summary: in Entity Framework I use TPC to create two classes derived from the same base class. In fluent API I map inherited properties, but how to model the properties of the base class?
More extensive description
In Entity Framework I have a class Child, and two kinds of Children: a Boy and a Girl. Both Boy and Girl derive from Child:
public class Child
{
public int Id {get; set;}
public string Name {get; set;}
}
public class Boy : Child
{
public string SomeBoyishProperty {get; set;}
}
public class Girl : Child
{
public string SomeGirlyProperty {get; set;}
}
I want a table with boys and a table with girls, each table also having the Child properties.
public class MyDbContext : DbContext
{
public DbSet<Boy> Boys {get; set;}
public DbSet<Girl> Girls {get; set;
}
From several sources, for example this one I learned that this is called TPC: table per concrete class and that I should MapInheritedProperties in OnModelCreating
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// model the properties of the base class, for instance set max length
modelBuilder.Entity<Child>()
.Property(p => p.Name).IsRequired().HasMaxLength(12);
// Model Daughter:
modelBuilder.Entity<Daughter>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Daughters");
})
.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13);
// model Boy
modelBuilder.Entity<Son>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Sons");
})
.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14);
}
During SaveChanges I get an InvlidOperationException indicating that the primary key is not unique. Removing the part that builds Child solves this problem.
How to build the Child properties without having to do this in the Girl and again in the Boy properties?
SHORT ANSWER:
If you want your code to work, remove any reference to Child entity in your model configuration. As soon as EF knows about Child as Entity it will enforce the following rule: There cannot be 2 entities of type Child or 2 entities that inherit from Child with the same PK in memory. You can see the error tells you that the entities where successfully persisted; but when EF pulls the new IDs it finds out both have the same ID.
LONG ANSWER
Remove
modelBuilder.Entity<Child>()
.Property(p => p.Name).IsRequired().HasMaxLength(12);
Instead this is how your OnModelCreating method should look like.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Model Daughter:
var girlEntity = modelBuilder.Entity<Girl>();
girlEntity.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Daughters");
});
girlEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
girlEntity.Property(p => p.Name).IsRequired().HasMaxLength(12);
girlEntity.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13);
// model Boy
var boyEntity = modelBuilder.Entity<Boy>();
boyEntity.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Sons");
});
boyEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
boyEntity.Property(p => p.Name).IsRequired().HasMaxLength(12);
boyEntity.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14);
}
If you don't want the configuration repetition in the configuration I would use the DataAnnotations attributes on the base class to enforce the name to be required.
You will also need to enforce that the Id property to be auto-generated in database. This doesn't happen by convention when using the Map method in the fluent API. You can see I added the fluent calls to make that happen in both the Girl and Boy mapping.
Hope this helps.
I reworked Arturo's solution proposal. This solution was too long to
describe as a comment. So Arturo: thanks for giving me the ideas.
Chapeau!
Arturo suggested to use Data Annotations. The reason that I don't want to use that method is, that the modelling of the class doesn't necessary correspond with a certain database representation. I bit hypothetical, but if I want a smaller maximum length for a boy's Name than for a Girl's name, then a data annotation wouldn't help.
Besides, there are some things that has to be done using fluent API. For instance, you can't say that a System.DateTime has a DateTime2 format in the database using DataAnnotations.
If you didn't guess it already: my problem description was highly simplified. All three classes have a lot of properties that need a lot of fluent API configurations
Arturo's remarks helped me to the following solution:
internal class ChildConfig<T> : EntityTypeConfiguration<T> where T : Child
{
public ChildConfig(...)
{
// configure all Child properties
this.Property(p => p.Name)....
}
}
internal class BoyConfig : ChildConfig<Boy>
{
public BoyConfig(...) : base (...)
{
// the base class will configure the Child properties
// configure the Boy properties here
this.Property(p => p.SomeBoyishProperty)...
}
}
And in MyDbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BoyConfig(...));
modelBuilder.Configuration.Add(new GirlConfig(...));
}
I work on a framework with EF. I want to get all ignored properties of an entity to build some special queries. How can I do it?
public class Customer
{
public int Id { get; set; }
public DateTime BirthDate { get; set; }
public int Age { get; set; }
}
public class CustomerContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().Ignore(customer => customer.Age);
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
}
public static class DbContextExtensions
{
public static List<string> GetIgnoredProperties(this DbContext context, string entityTypeName)
{
// ???
}
}
I know this is not answering your original question, and in my comments I mentioned that you should use reflection, but that was only because I read your question wrong.
Here is an alternative using reflection, for if you do not come right.
If you assign the [NotMapped] attribute to the properties on your class that you would like to ignore, you could possibly retrieve all [NotMapped] properties using reflection. Below is an example of how this could be achieved.
var resultArray = yourClassInstance.GetType().GetProperties()
.Where(prop => Attribute.IsDefined(prop, typeof(NotMappedAttribute)));
Hope this helps you in some way.
You can achieve what you want by calling the DbModelBuilder.Build. It will create a DbModel base on configuration setup by the DbModelBuilder. The DbModel expose a ConceptualModel that hold the types used by the context. The EdmModel hold each type that are declared in the context, and for each type, it hold the properties that has not been ignored by the DbModelBuilder during it's configuration. So, to achieve what you want, you have to intersect the properties of each entity type with those present in the EdmModel. It will give the delta between them, thefore the ignored properties. Here an example :
public class CustomerContext : DbContext
{
private static IReadOnlyDictionary<Type, IReadOnlyCollection<PropertyInfo>> _ignoredProperties;
/// Hold the ignored properties configured from fluent mapping
public static IReadOnlyDictionary<Type, IReadOnlyCollection<PropertyInfo>> IgnoredProperties
{
get
{
return _ignoredProperties;
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().Ignore(customer => customer.Age);
// Build ignored properties only if they are not
if (_ignoredProperties == null)
{
var model = modelBuilder.Build(this.Database.Connection);
var mappedEntityTypes = new Dictionary<Type, IReadOnlyCollection<PropertyInfo>>();
foreach (var entityType in model.ConceptualModel.EntityTypes)
{
var type = Type.GetType(entityType.FullName);
var typeProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var mappedProperties = entityType.DeclaredProperties.Select(t => t.Name)
.Union(entityType.NavigationProperties.Select(t => t.Name));
mappedEntityTypes.Add(type, new ReadOnlyCollection<PropertyInfo>(
typeProperties.Where(t => !mappedProperties.Contains(t.Name)).ToList()));
}
_ignoredProperties = new ReadOnlyDictionary<Type, IReadOnlyCollection<PropertyInfo>>(mappedEntityTypes);
}
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
}
The IgnoreProperties property is a singleton that will be initialized the first time you will use the context. It will be null before that, so will have to ensure that nothing use it until it's initialized. It's readonly, so you don't have to worrie about accidental clear of the collection. The entity type is used as key, and the value expose a collection that hold ignored properties. Example of use :
var properties = CustomerContext.IgnoredProperties[typeof(Customer)];
Cons :
With this approach is that the DbModel will be built twice, one time to gather the ignored properties, and second time by EntityFramework when the DbCompiledModel will be cached for futur ObjectContext creation. It can have an impact on the cold start of the DbContext, it means that the fist time you will execute a query over your context, it will be a bit slower. It will depend on the size of the DbContext. Warm queries should not suffer. OnModelCreating will be called once anyway.
Pros :
All changes made on de DbModelBuilder configuration will be automatically reflected in the IgnoredProperties property.
I'm trying to implement a TPC inheritance model in EF 4.3 CodeFirst for an existing Oracle database (over which I have no control). I have several sub-types that each map to its own table. Unfortunately, some of the key columns are of datatype number(18,0) instead of integer. EF seems to hate me now.
Here's my base class:
public abstract class Vehicle
{
public virtual int Id { get; set;}
public virtual string Color { get; set; }
//more properties
}
Here are some example sub-types:
public class Car : Vehicle
{
//more properties
}
public class Truck : Vehicle
{
//more properties
}
public class Motorcycle : Vehicle
{
//more properties
}
And here's my DbContet:
public class VehicleDataContext : DbContext
{
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<Car> Cars { get; set; }
public DbSet<Truck> Trucks { get; set; }
public DbSet<Motorcycle> Motorcycles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Vehicle>().HasKey(x => x.Id);
modelBuilder.Entity<Car>().Map(m => m.MapInheritedProperties());
modelBuilder.Entity<Car>().Property(x => x.Id).HasColumnType("decimal");
modelBuilder.Entity<Truck>().Map(m => m.MapInheritedProperties());
modelBuilder.Entity<Truck>().Property(x => x.Id).HasColumnType("int");
modelBuilder.Entity<Motorcycle>().Map(m => m.MapInheritedProperties());
modelBuilder.Entity<Motorcycle>().Property(x => x.Id).HasColumnType("decimal");
base.OnModelCreating(modelBuilder);
}
}
So, I already know to MapInheritedProperties so that all the properties of the base and sub-type are mapped to one table. I'm assuming that I have to tell the base that it HasKey so that EF doesn't complain that my DbSet<Vehicle> doesn't have a key mapped. I'd like to be able to assume that I can "tell" each entity how to map its own key's column type like I've done above. But I think that's not quite it.
Here's a test that fails:
[TestFixture]
public class when_retrieving_all_vehicles
{
[Test]
public void it_should_return_a_list_of_vehicles_regardless_of_type()
{
var dc = new VehicleDataContext();
var vehicles = dc.Vehicles.ToList(); //throws exception here
Assert.Greater(vehicles.Count, 0);
}
}
The exception thrown is:
The conceptual side property 'Id' has already been mapped to a storage
property with type 'decimal'. If the conceptual side property is
mapped to multiple properties in the storage model, make sure that all
the properties in the storage model have the same type.
As mentioned above, I have no control over the database and it's types. It's silly that the key types are mixed, but "it is what it is".
How can I get around this?
You cannot achieve it through mapping. This is limitation of EF code first. You can map each property (including the key) in inheritance structure only once. Because of that you can have it either integer or decimal in all entities in the inheritance tree but you cannot mix it.
Btw. what happens if you try to use int or decimal for the whole inheritance tree? Does it fail for loading or persisting entity? If not you can simply use the one (probably decimal if it can use whole its range) for all entities.
How do I make non persisted properties using codefirst EF4?
MS says there is a StoreIgnore Attribute, but I cannot find it.
http://blogs.msdn.com/b/efdesign/archive/2010/03/30/data-annotations-in-the-entity-framework-and-code-first.aspx
Is there a way to set this up using EntityConfiguration?
In EF Code-First CTP5, you can use the [NotMapped] annotation.
using System.ComponentModel.DataAnnotations;
public class Song
{
public int Id { get; set; }
public string Title { get; set; }
[NotMapped]
public int Track { get; set; }
Currently, I know of two ways to do it.
Add the 'dynamic' keyword to the property, which stops the mapper persisting it:
private Gender gender;
public dynamic Gender
{
get { return gender; }
set { gender = value; }
}
Override OnModelCreating in DBContext and remap the whole type, omitting the properties you don't want to persist:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>().MapSingleType(p => new { p.FirstName, ... });
}
Using method 2, if the EF team introduce Ignore, you will be able to easily change the code to:
modelBuilder.Entity<Person>().Property(p => p.IgnoreThis).Ignore();
If you don't want to use Annotations, you can use the Fluent API. Override the OnModelCreating and use DbModelBuilder's Ignore() method. Supposing you have a 'Song' entity:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Song>().Ignore(p => p.PropToIgnore);
}
}
You can also use EntityTypeConfiguration to move configurations to separate classes for better manageability:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new SongConfiguration());
}
}
public class SongConfiguration : EntityTypeConfiguration<Song>
{
public SongConfiguration()
{
Ignore(p => p.PropToIgnore);
}
}
I'm not sure if this is available yet.
On this MSDN page the Ignore Attribute and API are described but below, in the comments, somebody writes on 4 june 2010:
You will be able to ignore properties in the next Code First release,
Add
using System.ComponentModel.DataAnnotations.Schema
to the model class. (Must include "SCHEMA")
Add [NotMapped] data annotation to the field(s) you want to keep from persisting (ie. not save to database).
This will prevent them from being added as a column to the table in the db.
Please note - previous answers may have included these bits, but they did not have the full "using" clause. They merely left off "schema" - under which the NotMapped attribute is defined.