I think this is possible in nhiberate, but my question is about Entity Framework.
In my database model - which I cannot modify - I have redundant columns that I would like to store in different classes.
Example :
public class DateParams
{
public DateTime CreationDate { get; set; }
public DateTime ModificationDate { get; set; }
// some methods
}
public class Localization
{
public String EnglishLabel { get; set; }
public String FrenchLabel { get; set; }
// some methods
}
And then I would use them in some of my models :
public class Account // Localization && DateParams
{
public int ID { get; set; }
public String Name { get; set; }
public Localization Localization { get; set; }
public DateParams DateParams { get; set; }
}
public class Lead // DateParams only
{
public int ID { get; set; }
public String Name { get; set; }
public DateParams DateParams { get; set; }
}
What I would like to achieve is having something like this
public class LocalizationMap : EntityTypeConfiguration<Localization>
{
public LocalizationMap()
{
Property(e => e.EnglishLabel).HasColumnName("en");
Property(e => e.FrenchLabel).HasColumnName("fr");
}
}
public class AccountMap : EntityTypeConfiguration<Account>
{
public AccountMap()
{
HasKey(x => x.ID);
Property(e => e.Name).HasColumnName("Name");
HasSubMapping(new LocalizationMap());
HasSubMapping(new DateParamsMap());
ToTable("Account");
}
}
I could use inheritance to solve this, but C# does not allow multiple inheritance.
I'm not going to make you happy.
There is an EF feature called Table Splitting. As the name suggests, this allows us to map (split) one database table to multiple classes in the conceptual model. In your case, the mappings for Account would look like this:
class AccountMap : EntityTypeConfiguration<Account>
{
public AccountMap()
{
ToTable("Account");
HasKey(x => x.ID);
HasRequired(a => a.DateParams).WithRequiredPrincipal();
HasRequired(a => a.Localization).WithRequiredPrincipal();
}
}
class DateParamsMap : EntityTypeConfiguration<DateParams>
{
public DateParamsMap()
{
ToTable("Account");
}
}
class LocalizationMap : EntityTypeConfiguration<Localization>
{
public LocalizationMap()
{
ToTable("Account");
}
}
But this immediately shows the problem: the table name "Account" in the type configurations is hard coded. There's no way to reuse the satellite classes DateParams and Localization for multiple types. And, before you try, EF won't accept generics like DateParams<T>.
Which is sad, because all other options I can think of are ugly, or clunky at best:
Create subclasses of DateParams and Localization (and accompanying configurations) for any entity that needs them.
Just add the properties to all types and work with projections as much as possible (because I assume the whole point of this effort is to reduce the number of properties you're going to query).
Use one context hosting the main types without these properties and a second context hosting the satellite types (again, to help querying less properties easily). But unfortunately, you can only join the instances from both contexts in memory, i.e. LINQ to objects.
Create a third satellite class, combining both smaller classes, and use these three classes as base types.
You can achieve this by using complex types. These map to table columns named like complextypeName_propertyName but this behaviour can be changed by overwriting OnModelCreating(DbModelBuilder modelBuilder) in DbContext like described in Entity Framework - Reuse Complex Type
For your example:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Localization>();
modelBuilder.Entity<Account>().Property(x => x.Localization.EnglishLabel).HasColumnName("en");
modelBuilder.Entity<Account>().Property(x => x.Localization.FrenchLabel).HasColumnName("fr");
// et cetera
}
Related
I have entity that contain value-object with name IdCard like this:
public class UserProfile : BaseEntity<long>
{
public byte Gender { get; private set; } = 0;
public int? BirthDate { get; private set; }
public IdCard IdCard { get; set; }
}
And IdCard member like this:
public class IdCard : ValueObject
{
public int? Type { get; set; }
public string No { get; set; }
}
I need to make IdCard No as index by using EF fluent api
some thing like this
builder.HasIndex(c => c.IdCard.No);
Take a look at the Implement value objects from Microsoft and Using Value Objects with Entity Framework Core links. These two are helpful.
You can create UserProfileConfiguration class as follow:
public class UserProfileConfiguration : IEntityTypeConfiguration<UserProfile>
{
public void Configure(EntityTypeBuilder<UserProfile> builder)
{
builder.HasKey(x => x.Id);
builder.OwnsOne(x => x.IdCard);
builder.HasIndex(x => x.No);
}
}
And then, apply it in OnModelCreating method in your DbContext:
modelBuilder.ApplyConfiguration(new UserProfileConfiguration());
Ad constructor to Your IdCard that will take single parameter, in this case the No. Following should work for when the value-object have a single field that will be stored in database, so the Type would not have to be stored, but calculated after db fetch. Otherwise this would have to be stored as owned entity.
Alternatively you could store Type and No together in single field and provide custom converter for those fields, but this solution would be suboptimal to say the least.
In fluent configuration, assuming that try:
modelBuilder.Entity<UserProfile>(builder =>
{
builder.HasIndex(c => c.IdCard);
builder.Property(p => p.IdCard)
.HasConversion(id => id.No, value => new IdCard(value));
});
I do not remember if this will work in EF core without problems - there were some issues when it comes to converters used for primary keys in the past, but its worth a try.
I have following model, which (quite rightfully) does not work with Entity Framework Core. My problem is that I don't see an elegant solution right now, but I am sure this is a simple and quite generic construct, so I would appreciate a kick in the right direction.
Please see comments in the code for additional requirements that I have.
public abstract class Parent // requirement: must stay abstract
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Father : Parent
{
public List<Book> Books { get; set; }
}
public class Mother : Parent
{
public List<Book> Books { get; set; }
}
public class Book
{
public int Id { get; set;}
public string Title { get; set; }
public Parent Owner { get; set; } // Requirement: single Owner property; can be of Mother or Father Type
}
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
public DbSet<Mother> Mothers { get; set; }
public DbSet<Father> Fathers { get; set; }
public DbSet<Book> Books { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Father>()
.HasMany(s => s.Books)
.WithOne(s => (Father)s.Owner); // This was just an try
builder.Entity<Mother>()
.HasMany(s => s.Books)
.WithOne(s => (Mother)s.Owner); // This was just an try
builder.Entity<Book>()
.HasKey(t => t.Id);
}
}
Add-Migration does not go though with the message:
The corresponding CLR type for entity type 'Parent' cannot be instantiated, and there is no derived entity type in the model that corresponds to a concrete CLR type.
I thought so far of these potential solutions, but none qualifying:
Making Parent an Entity and moving the List<Book> Books there => No go
Giving up on navigation from Book to Owner => also No go
Splitting the Book class into two separate classes (e.g. FemaleBook & MaleBook) and managing two Entities instead of one. Very undesirable and leading to effectively doubling the code of the app => No go
A generic class? Book<Father>, Book<Mother>. Not sure, but I think that would also end up with two distinct Entities as of 3.
Something else you can help me with :-)
Remark: All classes have domain behavior methods which were omitted here for brevity. Father's a Mother's are unique (mix of override of Parent's and newly introduced).
PS: The app I am working on has nothing to do with Mothers, Fathers and Books. Just tried to provide minimum code example.
Many thanks for help.
EF Core does not support Complex types mapping.
If I had an object such as:
public class Entity {
public string StringProp {get; set;}
public SubEntity NestedEntity {get; set;}
}
where SubEntity is :
public class SubEntity{
public int IntProp {get; set;}
}
How could I map this to a table that has columns for StringProp and IntProp. Basically one record in the table is composed of the properties of both Entity and SubEntity.
I've tried ignoring SubEntity and exposing it's properties in Entity but that doesn't work because when NestedEntity is ignored any properties on Entity using its properties have no values.
Is there any other option than creating a class that has all the properties of the complex type or refactoring the database?
ComplexType mappings are now available since EF Core 2.0. There are currently two ways to do this that I am aware of.
Via Attributes
Not specifying the Column attribute may cause Entity Framework to not map properties to the correct column in existing tables without migrations.
e.g., it may map to Address_StreetAddress
using System.ComponentModel.DataAnnotations.Schema;
public class User
{
public int Id { get; set; }
// The complex property we want to use
public Address Address { get; set; }
public string UserName { get; set; }
}
// Lets Entity Framework know this class is a complex type
[ComplexType]
public class Address
{
// Maps the property to the correct column name
[Column("Address")]
public string StreeAddress { get; set; }
[Column("City")]
public string City { get; set; }
[Column("State")]
public string State { get; set; }
[Column("Zip")]
public string ZipCode { get; set; }
}
Via Fluent API
Not specifying the HasColumnName may cause Entity Framework to not map properties to the correct column in existing tables without migrations.
e.g., it may map to Address_StreetAddress
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class MyContext: DbContext
{
public DbSet<User> Users { get; set; }
public MyContext(DbContextOptions<MyContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>(table =>
{
// Similar to the table variable above, this allows us to get
// an address variable that we can use to map the complex
// type's properties
table.OwnsOne(
x => x.Address,
address =>
{
address.Property(x => x.City).HasColumnName("City");
address.Property(x => x.State).HasColumnName("State");
address.Property(x => x.StreetAddress).HasColumnName("Address");
address.Property(x => x.SuiteNumber).HasColumnName("SuiteNumber");
address.Property(x => x.ZipCode).HasColumnName("Zip");
});
});
}
}
I have decided to go with a class that contains all the relevant properties and
maps to all the required table columns.
It works fine as a workaround.
Will update when there is support for complex types in EF Core.
For example Project has references (1 optional, 1 required) to the User:
public class Project {
public User RequiredUser { get; set; }
public User OptionalUser { get; set; }
}
whereas User has many projects:
public class User {
public ICollection<Project> Projects { get; set; }
}
Would it be right to configure it like that:
modelBuilder.Entity<User>()
.HasMany(i => i.Projects)
.WithRequired(i => i.RequiredUser);
modelBuilder.Entity<Project>().HasOptional(i => i.OptionalUser);
From experience, if you set up separate relations (RequiredUser / OptionalUser), you'l also need separate IEnumerable<Project> properties in your User class.
public class User {
public ICollection<Project> Projects_Required { get; set; }
public ICollection<Project> Projects_Optional { get; set; }
}
Your setup then becomes:
modelBuilder.Entity<User>()
.HasMany(i => i.Projects_Required)
.WithRequired(i => i.RequiredUser);
modelBuilder.Entity<User>()
.HasMany(i => i.Projects_Optional)
.WithOptional(i => i.OptionalUser);
You could then add a AllProjects custom property that combines the two lists, if you so choose. However, it seems a dangerous thing to do, as you need to keep these lists separated because they address separate relations. To avoid confusion, I'd keep them separated as much as possible.
If there is a way to set up EF to have both relations end up in the same List property, I haven't found it. It seems hard and dangerous to implement.
I think you code would work, but not they way you expect it to:
ICollection<Project> Projects will only contain Project instances where the User is the RequiredUser.
I personally would use an intermediate table which has a IsRequired flag to map these two entities, but that changes the behaviour a little and introduces a not so easy to handle many-to-many relationship:
public class Project {
public ICollection<ProjectUser> Users { get; set; }
}
public class ProjectUser {
public Project Project { get; set; }
public User User { get; set; }
public bool IsRequired { get; set; }
}
public class User {
public ICollection<ProjectUser> Projects { get; set; }
}
The advantage is, that you can have more "optional users" (or required) in one project, but if your requirement is to only have on (required) user you would need to take care of this contraint in your business logic. You could also replace the bool with an enum if you have more then two possible "mapping types", e.g. Owner, Developer and Tester.
NOTE: If you have no use for this advantage you are better of with Flater's answer.
This seems to be a case of circular reference (http://en.wikipedia.org/wiki/Circular_reference) and definitely become a bad design. Think of the design why you need to refer to a User in Project which is already inside a User. Look more here: http://blogs.msdn.com/b/nickmalik/archive/2005/03/18/398601.aspx
Please consider the following scenario...
I have objects to define different types of products and the properties required to describe that product (e.g. a television will have different attributes to a perfume etc).
As such, I have the concept of a ProductType which has a List<> property with elements for the different fields (AttributeField<T>).
AttributeField<T> has a List<> property for the different values (AttributeValue<T>).
A product then has a ProductType to define the available fields (AttributeField<T>) and a List<AttributeValue<?>> for the values for this specific product.
I am trying to implement this concept in C#. I have the structure defined for the interfaces and objects but am struggling to map the objects using Fluent NHibernate, presumably because the ProductType is not a generic class but has a generic property (List<AttributeField<T>> where T can be anything).
Interfaces > IProductType
public interface IProductType
{
Guid Id { get; set; }
string Name { get; set; }
List<IAttributeField> Fields { get; set; }
}
Interfaces > IAttributeField / IAttributeField
public interface IAttributeField
{
Guid Id { get; set; }
string Name { get; set; }
IProductType ProductType { get; set; }
}
public interface IAttributeField<T> : IAttributeField
{
Guid Id { get; set; }
string Name { get; set; }
List<IAttributeValue<T>> Values { get; set; }
IProductType ProductType { get; set; }
}
Interfaces > IAttributeValue
public interface IAttributeValue<T>
{
Guid Id { get; set; }
T Value { get; set; }
IAttributeField<T> Field { get; set; }
}
Classes > ProductType
public class ProductType : IProductType
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<IAttributeField> Fields { get; set; }
}
Classes > AttributeField
public class AttributeField<T> : IAttributeField<T>
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<IAttributeValue<T>> Values { get; set; }
public IProductType ProductType { get; set; }
}
Classes > AttributeValue
public class AttributeValue<T> : IAttributeValue<T>
{
public Guid Id { get; set; }
public T Value { get; set; }
public IAttributeField<T> Field { get; set; }
}
I am using Fluent NHibernate for ORM with a SQL 2008 database and have the following mapping classes:
Classes > ProductTypeMapping
public class ProductTypeMapping : ClassMap<IProductType>
{
public ProductTypeMapping()
{
Not.LazyLoad();
Id(x => x.Id).GeneratedBy.GuidNative();
Map(x => x.Name).Length(50).Not.Nullable();
HasMany(x => x.Fields).CollectionType<IAttributeField>();
}
}
Classes > AttributeFieldMapping
public class GenericAttributeFieldMapping : ClassMap<IAttributeField>
{
public GenericAttributeFieldMapping()
{
Not.LazyLoad();
Id(x => x.Id);
Map(x => x.Name).Length(50).Not.Nullable();
References(x => x.ProductType).Cascade.All();
}
}
public class AttributeFieldMapping<T> : ClassMap<IAttributeField<T>>
{
public AttributeFieldMapping()
{
Not.LazyLoad();
Id(x => x.Id).GeneratedBy.GuidNative();
Map(x => x.Name).Length(50).Not.Nullable();
HasMany(x => x.Values).Cascade.AllDeleteOrphan();
References(x => x.ProductType).Cascade.All();
}
}
Classes > AttributeValueMapping
public class AttributeValueMapping<T> : ClassMap<IAttributeValue<T>>
{
public AttributeValueMapping()
{
Not.LazyLoad();
Id(x => x.Id).GeneratedBy.GuidNative();
Map(x => x.Value).CustomType<T>();
References(x => x.Field).Cascade.All();
}
}
When I try to unit test the above with a new PersitenceSpecification<ProductType> I get the following error:
Unit Test Adapter threw exception:
Type is not resolved for member 'FluentNHibernate.Cfg.FluentConfigurationException,FluentNHibernate, Version=1.3.0.733, Culture=neutral, PublicKeyToken=8aa435e3cb308880'..
When debugging further I get the following exception message:
No persister for: System.Collections.Generic.List`1[[Models.Interfaces.IAttributeField, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
Presumably this is because I cannot use generic types for mapping and need to provide mapping for each generic type to map against.
How can I have the structure detailed above work with dependency injection and ORM without having to explicitly provide mapping for each generic type OR what changes can be made to my approach to work with ORM whilst still providing the flexibility to have models with different attributes (fields and values)?
Fluent NHibernate is an Object Relation Mapper and persistence solution. It's not a dependency injection container.
It would be much easier to give a concrete answer with visibility of the database schema. However I understand this may not be practical.
It sounds to me like you are trying to create your application objects using Fluent NHibernate rather than just relying on it for persistence. This will probably result in some very complicated mapping as part of the ORM which may result in a lot of mapping classes. As stated here, you need a mapping class for each concrete type in your generics: Map generic EntityBase<TEntity> class with FluentNHibernate So it your example I think you will need one for every IAttributeField<T> and every IAttributeValue<T>
I would try and simplify the mapping classes as much as possible to match your data model and use a service layer with some factories or AutoMapper to construct your application objects. Separating your persisted entities from the application is not always advised (as it can add unnecessary layers and complexity - Macaroni code) but in a situation like this it seems like it might be necessary.
As I say, start at the data model and work outwards is best here and make sure you understand that Fluent NHibernate is just an ORM with many powerful features