Abstract Properties with a foreign key in Entity Framework 4.1? - entity-framework

UPDATE:
I updated the classes to look more like my actual model. When I remove the Assignable property from the Document base entity, everything works.
Is something like this possible to map in EF 4.1?
public abstract class Entity
{
public Guid Id {get;set;}
}
public abstract class Assignable:Entity
{
}
public class Contact: Assignable
{
public string Name {get;set;}
}
public class Partner: Assignable
{
public string Area {get;set;}
}
public abstract class Document: Entity {
public Guid AssignedToId {get;set}
public Assignable AssignedTo {get;set;
}
public class Submittal: Document
{
public string SomeProperty {get;set;}
}
Mapping:
public class EntityConfiguration<TEntity> : EntityTypeConfiguration<TEntity>
where TEntity : Entity
{
protected EntityConfiguration()
{
HasKey(e => e.Id);
Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class AssignableEntityMapping<TEntity>: EntityConfiguration<TEntity> where TEntity: Assignable
{
}
public class DocumentEntityMapping<TEntity>: EntityConfiguration<TEntity>
{
public DocumentEntityMapping()
{
HasOptional(e => e.AssignedTo).WithMany().HasForeignKey(e => e.AssignedToId);
}
}
public class ContactMapping: AssignableEntityMapping<Contact>
{
....
}
public class PartnerMapping: AssignableEntityMapping<Partner>
{
....
}
public class SubmittalMapping: DocumentEntityMapping<Submittal>
{
....
}
If this is possible how do I setup the mapping so that the AssignedToId is a foreign key to one of the concrete entities? I created a mapping for the Document entity and I get the error: "The property 'Id' is not a declared property on type..." I am guessing it is because EF doesn't know how to discriminate what the AssignedToId refers to?

I had a lot of extra architecture around the Contact and Partner entities that I could not use something like a TPH (Table Per Hierarchy) approach. EF doesn't know how to map the AssignedToId in the Document class since i was trying to use Table per Concrete Type (TPC) at the same time. I ended up just adding all the "assignable" types to the Document class for now. If there is another way around this, I'd like to know.
public abstract class Document: Entity
{
public Guid? AssignedContactId {get;set;}
public Contact AssignedContact {get;set;}
public Guid? AssignedPartnerId {get;set;}
public Partner AssignedPartner {get;set;}
}

Related

How do you implement a base and derived class where primary key is defined in the base class

Given the following code example, how do you implement this in entity framework core where you do not want a table created for the base class but you do for the derived class and the primary key is defined in the base class?
public class JobBase
{
public JobBase() { }
public Guid JobId { get; set; } = Guid.NewGuid();
public string Title { get; set; }
}
public class Job : JobBase
{
public Job() { }
public String AdditionalInformation { get; set; }
}
And here is what I have in my DBContext class:
public DbSet<Job> Jobs { get; set; }
var job = mb.Entity<Job>();
job.HasKey(aa => aa.JobId);
job.Property(aa => aa.JobId).HasColumnName("JobId");
I currently get the following error when trying to add-migration:
A key cannot be configured on 'Job' because it is a derived type. The key must be configured on the root type 'JobBase'. If you did not intend for 'JobBase' to be included in the model, ensure that it is not referenced by a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation on a type that is included in the model.
The only part of the error message I am doing is JobBase is referenced from a navigation on a type that is included in the model but doing that is the whole reason i implemented this relationship in the first place so i can't just remove that navigation.
modelBuilder.Entity<Job>().HasKey(x => x.JobId).ToTable("Job");
EF Inheritance and Primary Keys

How to use AOP (PostSharp) for Serialization?

Is it possible to inject code to read/write the properties of an object using a PostSharp aspect? For example, consider the following class:
[ BinarySerializable ]
public class Employee {
public string Name {get; set; }
public string Title {get; set;}
}
In this case, "BinarySerializable" would be a custom aspect that introduces a custom "IBinarySerializable" interface, as follows:
public interface IBinarySerializable
{
void Write(BinaryWriter writer);
void Read(BinaryReader reader);
}
After compilation, the resulting class would look like this:
public class Employee : IBinarySerializable
{
public string Name {get; set;}
public string Title {get; set; }
void IBinarySerializable.Write(BinaryWriter writer)
{
writer.Write(Name);
writer.Write(Title);
}
void IBinarySerializable.Read(BinaryReader reader)
{
Name = reader.ReadString();
Title = reader.ReadString();
}
}
Intuitively, I feel this should be possible using PostSharp, but I need some direction as to the right approach. If this is possible, then how to handle properties that are themselves injected by some other aspect?
UPDATE: I tried creating a simple example using the built-in PSerializable aspect, but ran into problems when members inherit from .NET framework classes that do not have that attribute.
Adding the [PSerializable] attribute to the EmployeeCollection class fails to compile with "Cannot apply [PSerializable] to type 'AOPSerialization.EmployeeCollection' because the base type does not have a [PSerializable] or [Serializer] attribute."
Omitting the [PSerializable] attribute from the EmployeeCollection class throws a runtime PortableSerializationException: Cannot find a serializer for type 'AOPSerialization.EmployeeCollection'.
For example:
[PSerializable]
public class AOPComponent
{
public string Title { get; set; }
public string Description { get; set; }
public AOPComponent(string title, string description){...}
}
[PSerializable]
public class AOPComponentCollection<T> : ObservableCollection<T>
{...}
[PSerializable]
public class EmployeeCollection : AOPComponentCollection<Employee>
{...}
[PSerializable]
public class Company : AOPComponent
{
public EmployeeCollection Engineers { get; set; }
public EmployeeCollection Managers { get; set; }
}
I figured out that the Serializer and ImportSerializer attributes are used to tell the PortableFormatter which custom ISerializer or ISerializerFactory implementation to use.
But the question remains:
How to specify a custom serializer for the generic base collection type?
This approach fails because an attribute may not include type parameters.
[PSerializable, ImportSerializer(typeof(ObservableCollection<T>), typeof(AOPComponentSerializerFactory))]
public class AOPComponentCollection<T> : ObservableCollection<T> where T : AOPComponent
{...}
This approach fails because PostSharp cannot find a serializer for ObservableCollection< T >
[PSerializable, Serializer(typeof(AOPComponentSerializerFactory))]
public class AOPComponentCollection<T> : ObservableCollection<T> where T : AOPComponent
{...}
It would be possible to do that with PostSharp, but only by using the low-level PostSharp SDK, which is undocumented and unsupported.
Good news are that we already implemented this for you, in the namespace PostSharp.Serialization. The aspect is [PSerializable] and the formatter is PortableFormatter.

Code first Type per hierarchy

I use EF 5 with the code first approach. Now I try to define a "code" table in which I want to have several different codes (like address code, medium code, etc.). In this table I just have the following properties: ID (Guid), Name (String), Description (String) and a discriminator (in this case something like the type of the code: address code, medium code, etc.).
So I defined the following base class:
public abstract class Code : EntityBase
{
public string Name { get; set; }
public string Beschreibung { get; set; }
}
Then I derived two classes from code
public class AddressCode : Code {}
public class MediumCode : Code {}
The class EntityBase is abstract and just defines the Id property, we use it for every POCO class...
The goal is that I can use AddressCode as a property on my address POCO class:
public class Adresse : EntityBase
{
#region Properties
public string Name1 { get; set; }
public virtual AddressCode AddressCode { get; set; }
#endregion
}
The question now is, how can I explain EF how to do that? Anyone can help?
Thanks
Marco
Thanks for your answer!
I tried to do it like you said. Unfortunately I get an error because of my EntityBase class:
public abstract class EntityBase
{
#region Properties
public virtual Guid Id { get; set; }
public virtual bool IsValid
{
get
{
{
return Validate();
}
}
}
[NotMappedAttribute]
public virtual IList<ValidationFailure> ValidationFailures { get; set; }
#endregion
#region Methods
private bool Validate()
{
var validatorFactory = new AttributedValidatorFactory();
IValidator validator = validatorFactory.GetValidator(GetType());
if (validator == null)
{
return true;
}
ValidationResult validationResult = validator.Validate(this);
ValidationFailures = validationResult.Errors;
return validationResult.IsValid;
}
#endregion
}
The error message is:
You cannot use Ignore method on the property 'ValidationFailures' on type 'Entities.AdresseCode' because this type inherits from the type 'Entities.EntityBase' where this property is mapped. To exclude this property from your model, use NotMappedAttribute or Ignore method on the base type.
As you can see I already defined the property ValidationFailures as NotMapped but still I get this error.. Do you have an idea?
Thanks
Marco
Just create a context (derived from DbContext)
public class AddressesDb : DbContext
{
public DbSet<Code> Codes { get; set; }
public DbSet<Adresse> Adressen { get; set; }
}
And (when used in code) EF will create a database with default table and column names. It will create a discriminator column of type text (nvarchar) which will contain the names of the classes that derive from Code.
If you want different names and/or types you should either use data annotations or fluent API to configure these.
Finally I got it work!
In the DBContext be aware to define DbSets for the code derived classes before all the other POCO's and then it works!

How do I do a many to many relationship in Entity Framework 4.3?

I have the following classes:
public class Entity
{
public long Id {get;set;}
}
public abstract class Base
{
public long Id {get;set;}
public abstract ICollection<Entity> Entities {get;set;}
}
public class Child : Base
{
public override ICollection<Entity> Entities {get;set;}
}
The mapping:
modelBuilder.Entity<Child>().Map(
m => {
m.ToTable("Children");
m.MapInheritedProperties();
});
modelBuilder.Entity<Child>().HasMany(m => m.Entities).WithMany();
I get the following exception:
The navigation property 'Entities' is not a declared property on type
'EventCriteria'. Verify that it has not been explicitly excluded from the model
and that it is a valid navigation property.
What am I doing wrong?
You are mapping TPC inheritance (you call MapInheritedProperties) and in that case Entities collection must be mapped in your Base entity, not in Child entity.

How to create an Entity Data Model for inherited generic types?

I have no clue how i can get an existing object structure based on the following classes (simplified) into a database using Entity Framework (EF is a constraint, i have to use it).
public abstract class WahWahProperty
{
public string Name { get; set; }
public abstract Type PropertyType { get; }
}
// ----------------
public class WahWahProperty<T> : WahWahProperty
{
public T Value { get; set; }
public override Type PropertyType
{
get { return typeof(T); }
}
}
// ----------------
public class WahWahContainer
{
public List<WahWahContainer> Children { get {...}; }
public List<WahWahContainer> Parents { get {...}; } // multiple "Parents" allowed
public List<WahWahProperty> Properties { get {...}; }
//... some more props here ...
}
Any ideas?
The EF doesn't support generic Entity types (which seems to be what you are doing).
Although we have made a change in EF 4.0 (not in Beta1) so you will be able to use a non-generic class derived from a generic class as an Entity.
Anyway hope this helps
Alex
Program Manager Entity Framework Team
Entity Framework Tips