DataAnnotations vs .Map Functions Database Model Mapping? - c#

Which one is better and why?
[Table("Bar")]
public class Bar {
[Key]
public Int32 BarId { get; set; }
public String BarName { get; set; }
public IEnumerable<Foo> Foos { get; set; }
}
or
public class BarMap : EntityTypeConfiguration<Bar> {
public BarMap() {
this.HasMany(t => t.Foos)
.WithMany(t => t.Bars)
.Map(m => {
m.ToTable("FooBarRelationships");
m.MapLeftKey("TheBarId");
m.MapRightKey("TheFooId");
});
}
}
I know the first one is called DataAnnotations but don't know how can we call the second type.

The second is called Fluent API (fluent configuration).
From my point of view, I would prefer the second option which can separate lots of Attributes out of models. It would make the models cleaner.

Related

nhibernate mapping by code approach, mapping object

I have following model
public class Car : Entity<int>
{
public virtual int Id{ get; set; }
...
public virtual Engine Engine { get; set; }
}
and I'm using nhibernate mapping by code approach
public class CarMap : ClassMapping<Car>
{
public CarMap()
{
Id(x => CarId, m => m.Generator(Generators.Identity));
// how map reference Engine?
**// edit**
HasOne(x=>x.Engine, m=>{}) // is this good enough?
}
}
how map Engine in this CarMap object?
You need a little more information in the question, but here's a couple of options.
Is this really a one to one relationship? One to one relationships are somewhat unique in that both sides of the relationship tend to share the same Id. Like David Osborne said you most likely want a One to Many relationship. But you you want it to be bi-directional? i.e. you can navigate down from the Engine to all the cars that may have that engine or up from the car to a specific engine. i.e. engine is Chrysler Hemi engine 5.7L and it is in the cars, Ram Pickup, Dodge Durango, Dodge Charger.
Then you may want to map the objects like this
public class Engine : Entity<int>
{
public Engine()
{
Cars = new List<Car>();
}
public virtual int Id { get; protected set; }
public virtual decimal Displacement { get; set; }
//more properties
public virtual IList<Car> Cars { get; }
public virtual void AddCar(Car car)
{
if (Cars.Contains(car)) return;
Cars.Add(car);
}
public virtual void RemoveCar(Car car)
{
if (!Cars.Contains(car)) return;
Cars.Remove(car);
}
}
public class Car : Entity<int>
{
public virtual int Id { get; set; }
public virtual Engine Engine { get; set; }
}
So if you are mapping the Engine you need to define the Cars mapping list this
Bag(x => x.Cars, map =>
{
map.Key(k => k.Column(col => col.Name("EngineId")));
map.Cascade(Cascade.All | Cascade.DeleteOrphans); //optional
},
action => action.OneToMany());
and the other side of the relationship like this
ManyToOne(x => x.Engine, map =>
{
map.Column("EngineId");
map.NotNullable(true); // if you require the Engine to be in a car
});
If you just want a one way mapping from the car to the engine, just remove all the references to the Cars list and delete the Bag mapping.
Using Fluent NH, I would use References(), which has a mapping-by-code equivalent of ManyToOne() apparently.

Entity Framework Core not supporting generic abstract entities with many to many relationships

I have faced a strange problem witch EF Core 1.1. I m trying to build application where some entities can be tagged, thus I've created an abstract generic class for the relation table list. The problem is that, it seems like EF do not support to have a generic abstract classes which FK (Id property works).
Here are models:
public abstract class TaggedEntityBase<T> : EntityBase
{
public ICollection<T> EntityTags { get; set; }
public List<Tag> Tags { get { return EntityTags?.Select(x => x.Tag).ToList(); } }
}
public class AddressTag
{
public long TagId { get; set; }
public Tag Tag { get; set; }
public long EntityId { get; set; }
public Address Entity { get; set; }
}
public class Address : TaggedEntityBase<AddressTag>
{
public string Street { get; set; }
public string City { get; set; }
}
public class Tag : EntityBase
{
public string Name { get; set; }
public virtual ICollection<AddressTag> AddressTags { get; set; }
}
The Model Builder mappings:
public DbSet<Address> Addresses { get; set; }
public DbSet<AddressTag> AddressTag { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AddressTag>()
.ToTable("AddressTag");
modelBuilder.Entity<AddressTag>()
.HasKey(t => new { t.EntityId, t.TagId });
modelBuilder.Entity<AddressTag>()
.HasOne(pt => pt.Entity)
.WithMany(p => p.EntityTags)
.HasForeignKey(p => p.EntityId);
modelBuilder.Entity<AddressTag>()
.HasOne(pt => pt.Tag)
.WithMany(p => p.AddressTags)
.HasForeignKey(p => p.TagId);
}
There is an error when EF try to fetch Tags
An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.dll Additional information: Invalid column name 'AddressId'.
I dont even have that Id convention.
Note: when I place explicitly public ICollection<AddressTag> EntityTags { get; set; }inside Address POCO, then it works perfectly, including EntityTags.Tag too.
Thanks for any help :)
The issue has nothing to do with generic and/or abstract base entity classes.
First, to make your sample model compile, I've added the following classes
public abstract class EntityBase
{
public long Id { get; set; }
}
public abstract class EntityTagBase
{
public long TagId { get; set; }
public Tag Tag { get; set; }
}
modified the AddressTag class as follows:
public class AddressTag : EntityTagBase
{
public long EntityId { get; set; }
public Address Entity { get; set; }
}
and added where T : EntityTagBase constraint to TaggedEntityBase<T> class to allow Tag property accessor inside Select(x => x.Tag).
So far so good. The Tag related part of generated migration looks like this:
migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
AddressId = table.Column<long>(nullable: true),
Name = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
table.ForeignKey(
name: "FK_Tags_Addresses_AddressId",
column: x => x.AddressId,
principalTable: "Addresses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
See the AddressId column and FK to Addresses table? Why is that? Because of your Tags property:
public List<Tag> Tags { get { return ...; } }
It's probably a current EF Core bug of mapping a read only collection property, but the net effect is that it considers one to many relationship between Address and Tag which of course is not your intention.
In general I would recommend keeping the entity model clean and not include such "helper" properties - both collection and reference type. They look like navigation properties, but they are not, and it's easy to use them by mistake inside a query, which will totally change the execution plan and lead to unexpected exceptions or wrong results (in case the underlying property is not loaded). Not speaking about the violation of a general rule to not create property returning List which is not a member of the class, but created in every property access call.
Shortly, simply remove that property and the problem will be gone. Or if you insist keeping it, then decorate it with NotMapped data annotation:
[NotMapped]
public List<Tag> Tags { get { return ...; } }

Entity Framework : map multiple classes to one table

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
}

Entity Framework One-Many TPH Mapping

I'm using a data structure similar to this where type of animal is determined from a discriminator column in the table:
public class Farm {
public int Id { get; set; }
public virtual ICollection<Pig> Pigs { get; set; }
public virtual ICollection<Cow> Cows { get; set; }
}
public class Animal {
public int Id { get; set; }
public int FarmId? { get; set; }
public virtual Farm Farm { get; set; }
public string Name { get; set; }
}
public class Pig : Animal {}
public class Cow : Animal {}
Mapping:
this.Map<Pig>(m => m.Requires("Type").HasValue((int) AnimalType.Pig));
this.Map<Cow>(m => m.Requires("Type").HasValue((int) AnimalType.Cow));
But I can't seem to map the relationship between the Pigs, Cows and Farm. I've tried this from FarmMap which gives a duplicate column mapping error:
this.HasMany(t => t.Pigs)
.WithOptional(t => t.Farm)
.Map(m => m.MapKey("FarmId"));
this.HasMany(t => t.Cows)
.WithOptional(t => t.Farm)
.Map(m => m.MapKey("FarmId"));
Mapping from each of the animals doesn't work either, it generates extra columns (eg. Farm_Id and Farm_Id1 - in addition to FarmId - one for each animal type).
this.HasOptional(t => t.Farm)
.WithMany(t => t.Pigs)
.HasForeignKey(d => d.FarmId)
Moving the navigation property from the Animal model to the inheriting models causes a single additional column to be generated - FarmId1 (so a little closer to what I want than the above 2!)
Is there any way to achieve this?
I'm no EF expert but from the Model-first approach I know that this would be mapped as a collection of Animal, you can then select Farm.Animals.OfType<Pig>()

Using dependency injection on a class with a list<interface<T>> property where T is unknown

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

Categories

Resources