I have the following:
public abstract class InputBase
{
public virtual ICollection<Data> Data { get; set; }
}
public class InputA: InputBase { }
public class InputB: InputBase { }
public abstract class Data
{
public virtual InputA InputA { get; set; }
public virtual InputB InputB { get; set; }
}
InputA and InputB both use InputBase's collection of Data.
Data will have an instance of InputA and InputB at all times.
I tried linking this up via:
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputA)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputB)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputA>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputA)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputB>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputB)
.WillCascadeOnDelete(false);
However, I'm getting a MetaDataException, error 0040: Type Data_InputA is not defined in namespace (...)
How can I make this work? I don't want to have to create separate Data collections on the Input derivates as that wouldn't be logically correct.
This should fix your problem:
Change this:
public abstract class Data
{
public virtual InputA InputA { get; set; }
public virtual InputB InputB { get; set; }
}
To: If you are worried about the foreign key, its alright. Key of InputA Object is the same as InputBase Object since we are using Inheritance. Hence, Data Structure dictates that we do the following.
public abstract class Data
{
[ForeignKey("InputBase"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int? InputBaseId { get; set; }
public virtual InputBase InputBase { get; set; }
}
If your Inheritance is Table per Type (TPT),
Do this to derived classes:
[Table("InputB")] //This is what your table will be named in your database for derived class
public class InputA: InputBase { }
[Table("InputB")]
public class InputB: InputBase { }
Also you don't need this:
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputA)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputB)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputA>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputA)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputB>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputB)
.WillCascadeOnDelete(false);
You can remove that section because we have used Property Mapping.
Related
In Entity Framework with Fluent Configuration, I have a LeaseTrackingType entity which has a one to many relationship with LeaseTracking. Where each lease tracking has a lease tracking type.
However from a code point of view it doesn't really make sense to have a LeaseTrackings collection. Like you are never going to use this navigation property.
Question: How do I model the one to many relationship without LeaseTrackings collection navigation property?
Entities:
public class LeaseTracking
{
public int Id { get; set; }
public int LeaseTrackingTypeId { get; set; }
public LeaseTrackingType LeaseTrackingType { get; set; }
}
public class LeaseTrackingType
{
public int Id { get; set; }
public string Name { get; set;}
public virtual Collection<LeaseTracking> LeaseTrackings { get; set;}
}
And mapping configuration:
public class LeaseTrackingConfiguration : EntityTypeConfiguration<LeaseTracking>
{
public LeaseTrackingConfiguration()
{
ToTable("LeaseTracking");
Property(entity => entity.Id);
HasRequired(entity => entity.LeaseTrackingType)
.WithMany(entity => entity.LeaseTrackings)
.HasForeignKey(entity => entity.LeaseTrackingTypeId);
}
}
public class LeaseTrackingTypeConfiguration : EntityTypeConfiguration<LeaseTrackingType>
{
public LeaseTrackingTypeConfiguration()
{
ToTable("LeaseTrackingType");
Property(entity => entity.Id);
Property(entity => entity.Name).;
}
}
in the constructor don't point it out in WithMany function and get rid of the virtual Collection in LeaseTrackingType class
public LeaseTrackingConfiguration()
{
ToTable("LeaseTracking");
Property(entity => entity.Id);
HasRequired(entity => entity.LeaseTrackingType)
.WithMany()
.HasForeignKey(entity => entity.LeaseTrackingTypeId);
}
I'm trying to map my entities following the TPC pattern.
I have an abstract base class Agent :
public abstract class Agent
{
public int Id { get; set; }
public DateTime? ChangeDate { get; set; }
public DateTime CreationDate { get; set; }
public string insuranceCompanyPolicyNumber { get; set; }
public int? KeySys { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
public string Status { get; set; }
public int? Transactionid { get; set; }
}
I have a child entity Suscriber :
public partial class Suscriber : Agent
{
public int? id_federateur { get; set; }
public string Number { get; set; }
public bool? suivi_client { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
}
When i try to configure the Suscriber like the following, i've noticed that the request generated by Entity Framework is wrong :
public class SuscriberConfiguration : EntityTypeConfiguration<Suscriber>
{
public SuscriberConfiguration()
{
Map(m =>
{
m.MapInheritedProperties();
m.Property(s => s.Number).HasColumnName("numero_souscripteur");
// Property(s => s.Number).HasColumnName("numero_souscripteur");
m.ToTable("T_ACTEUR_SOUSCRIPTEUR");
});
}
}
The request generated by Entity Framework is (The table Agent2 doesn't exist):
SELECT E'0X0X' AS "C1",
"Extent1"."id_personne",
"Extent1"."suivi_client",
"Extent1"."id_federateur",
"Extent2"."date_mod",
"Extent2"."date_cre",
"Extent2"."insuranceCompanyPolicyNumber",
"Extent2"."keySys",
"Extent2"."id_role",
"Extent2"."statut",
"Extent2"."vsc_transactionid",
"Extent2"."numero_souscripteur"
FROM "atlas"."Agent2" AS "Extent1"
INNER JOIN "atlas"."T_ACTEUR_SOUSCRIPTEUR" AS "Extent2" ON
"Extent1"."id_personne" = "Extent2"."id_personne"
I know that if i changed the configuration of suscriber like this, it will work
public class SuscriberConfiguration : EntityTypeConfiguration<Suscriber>
{
public SuscriberConfiguration()
{
Map(m =>
{
m.MapInheritedProperties();
// m.Property(s => s.Number).HasColumnName("numero_souscripteur");
Property(s => s.Number).HasColumnName("numero_souscripteur");
m.ToTable("T_ACTEUR_SOUSCRIPTEUR");
});
}
}
In other words, if i configure the property Number outside the scope of the method Map or i don't apply the method Property to the parameter of the lambda, it works. (Property(s => s.Number).HasColumnName("numero_souscripteur");)
Could somebody explain me how the method MapInheritedProperties() works?
Why Entity Framework generate a non existing table?
Thanks
This is because everything that you put in one mapping fragment...
Map(m => { ... })
...is mapped to the table that the fragment applies to. In fact it's saying: map all properties from Agent and property Number from Subsriber to table "T_ACTEUR_SOUSCRIPTEUR". EF silently assumes that the leftovers are mapped to another table it devices by naming conventions. This happens as soon as you map one property from the derived type. If you only use MapInheritedProperties(), all properties, including the derived ones, are mapped to the table in the mapping fragment.
I don't know if this is a bug or a feature. It's not very clear at least. Anyway, you should map other property names outside the mapping fragment, as you already found out. Of course it's it more clear if you do that in a separate statement:
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("T_ACTEUR_SOUSCRIPTEUR");
});
Property(s => s.Number).HasColumnName("numero_souscripteur");
Or if you like:
Map(m => m.MapInheritedProperties());
ToTable("T_ACTEUR_SOUSCRIPTEUR");
Property(s => s.Number).HasColumnName("numero_souscripteur");
I am working to map an existing database using Fluent NHibernate and have encountered a problem when it comes to complex many-to-many relationships (additional columns).
I know that many-to-many relationships with additional columns have to be mapped as HasMany rather than HasManyToMany as they are not pure many-to-many relationships. The linking table has to be mapped as a class within itself, which I have done in the example below.
When loading this data from an existing database it loads fine. The project I am working on takes this data and inserts it into an empty database, which is where the problem occurs. I think that when inserting into the new database the CompositeId is trying to insert NULL values for ItemID and ItemGroupID which is not allowed in the database. Changing the database structure is not a viable option at this point, is there a way around this issue?
Thanks, example code below.
Entity Classes
public class Item
{
public virtual long ItemID { get; set; }
public virtual string Name { get; set; }
}
public class ItemGroup
{
public virtual long ItemGroupID { get; set; }
public virtual string Name { get; set; }
public virtual IList<ItemInGroup> ItemsInGroup { get; set; }
}
public class ItemInGroup
{
public virtual Item Item { get; set; }
public virtual ItemGroup ItemGroup { get; set; }
public virtual int? DisplayOrder { get; set; }
}
Mapping Classes
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Table("Items");
Id(x => x.ItemID).GeneratedBy.Identity();
Map(x => x.Name);
}
}
public class ItemGroupMap : ClassMap<ItemGroup>
{
public ItemGroupMap()
{
Table("ItemGroups");
Id(x => x.ItemGroupID).GeneratedBy.Identity();
Map(x => x.Name);
HasMany(x => x.ItemsInGroup).KeyColumn("ItemGroupID").Cascade.All();
}
}
public class ItemInGroupMap : ClassMap<ItemInGroup>
{
public ItemInGroupMap()
{
Table("ItemsInGroups");
CompositeId().KeyReference(x => x.Item, "ItemID")
.KeyReference(x => x.ItemGroup, "ItemGroupID");
Map(x => x.DisplayOrder);
}
}
assuming DisplayOrder is the only extra column in the link table why not use the List index of IList as order?
public class ItemGroup
{
public virtual long ItemGroupID { get; set; }
public virtual string Name { get; set; }
public virtual IList<Item> Items { get; private set; }
}
public class ItemGroupMap : ClassMap<ItemGroup>
{
public ItemGroupMap()
{
Table("ItemGroups");
Id(x => x.ItemGroupID).GeneratedBy.Identity();
Map(x => x.Name);
HasManyToMany(x => x.ItemsInGroup)
.Table("ItemsInGroups")
.ParentKeyColumn("ItemGroupID")
.ChildKeyColumn("ItemID")
.AsList("DisplayOrder")
.Cascade.All();
}
}
I have 2 master tables which are linked by a map table as below
User [UserId,Name]
Resource [ResourceId,Name]
UserResourceMap [UserId,ResourceId,AccessLevel]
How would the Resource and User ClassMap with AccessLevel as a resource attribute look?
My Domain classes look like this
public class User
{
public virtual int UserId { get;protected set; }
public virtual string Name { get;set; }
}
public class Resource
{
public virtual int ResourceId { get;protected set; }
public virtual string Name { get;set; }
public virtual string AccessLevel { get;set; }//Issue-populate this using fluent
}
How can I use fluent to map the accessLevel attribute in the below code.
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("User");
Id(x => x.Key);
Map(x=>x.Name);
}
}
public class ResourceMap : ClassMap<Resource>
{
public ResourceMap()
{
Table("Resource");
Id(x => x.Key);
Map(x=>x.Name);//Need some Map Here to make a hasManyToMany Map with attribute
}
}
Your domain model does not seem to match your database model - the Resource class has the property AccessLevel (i.e. one AccessLevel per Resource) but in the DB model AccessLevel is a column on the map table (i.e. one AccessLevel per User-Resource relation).
Assuming the DB model is the correct model one (fairly straightforward) way of mapping this would be to introduce a class like this.
public class UserResource {
public virtual int UserResourceId { get; protected set; }
public virtual User User { get; set; }
public virtual Resource { get; set; }
public virtual string AccessLevel { get; set; }
}
and map it in this way:
public class UserResourceMap : ClassMap<UserResource> {
public UserResourceMap() {
Table("UserResourceMap");
Id(x => x.UserResourceId);
References(x => x.User).UniqueKey("UniqueUserAndResource");
References(x => x.Resource).UniqueKey("UniqueUserAndResource");
Map(x => x.AccessLevel);
}
}
If you want bidirectional associations you could also add a Collection property on User and/or Resource and map these with HasMany(...).Inverse(). Of course, this kind of mapping would introduce a new UserResourceId column in the UserResourceMap table (using a composite key consisting of User and Resource would mitigate that).
Another solution would be to add an EntityMap association. If the association is owned by User it would be a Dictionary<Resource, string> property. Something like this might do the trick:
public class User {
public virtual int UserId { get; protected set; }
public virtual string Name { get; set; }
public virtual Dictionary<Resource, string> Resources { get; set; } // Resource -> AccessLevel
}
public class UserMap : ClassMap<User> {
public UserMap() {
Table("User");
Id(x => x.UserId);
Map(x => x.Name);
HasMany<Resource, string>(x => x.Resources).AsEntityMap().Element("AccessLevel");
}
}
As you've correctly identified in your database schema, this isn't a pure many-to-many relationship - it's two one-to-many relationships as the intermediate table has an attribute (the access level).
I therefore think your domain is missing an entity - there doesn't appear to be any relationship in your model between a user and the resources they can access.
How about something like this:
public class User
{
public virtual int Id { get;protected set; }
public virtual string Name { get;set; }
public virtual ICollection<UserResource> UserResources { get; set;}
}
public class UserResource
{
public virtual int Id { get; protected set; }
public virtual User User { get; set;}
public virtual Resource Resource { get; set;}
public virtual string AccessLevel { get; set;}
}
public class Resource
{
public virtual int Id { get;protected set; }
public virtual string Name { get;set; }
}
And mappings like:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.UserResource)
.AsSet()
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
public class UserResourceMap : ClassMap<UserResource>
{
public UserResourceMap()
{
Table("UserResourceMap");
Id(x => x.Id);
References(x => x.User).Not.Nullable();
References(x => x.Resource).Not.Nullable();
Map(x => x.AccessLevel);
}
}
public class ResourceMap : ClassMap<Resource>
{
public ResourceMap()
{
Cache.ReadOnly();
Id(x => x.Id);
Map(x => x.Name);
}
}
I am using IQueryable<> to build up batching queries.
I have used views successfully to fetch information so the IQueryable<> can find it, but in this instance I can't work out how to map a view, as it depends on properties rather than the entity's ID.
Say I have this entity and mapping:
public class Calculation
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual TransactionTotal TransactionTotal { get; set; }
}
public class CalculationMap : ClassMap<Calculation>
{
public CalculationMap()
{
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.Organisation).Not.Nullable().UniqueKey("OC");
References(x => x.Charge).Not.Nullable().UniqueKey("OC");
}
This is the class I need to get in there: I'm using a view to give me the total amount per Organisation and Charge:
public class TransactionTotal
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual decimal Amount { get; set; }
}
public class TransactionTotalMap : ClassMap<TransactionTotal>
{
public TransactionTotalMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Table("TransactionTotalsView");
References(x => x.Charge).Not.Nullable();
References(x => x.Organisation).Not.Nullable();
Map(x => x.Amount).Precision(15).Scale(2).Not.Nullable();
}
}
Other places I have used views I have successfully used mappings like HasOne(x => x.TransactionTotal); but in this instance I need to tell Nhibernate to use the Organisation and Charge properties as the key.
Is this even possible? If so, how do I map the TransactionTotal class to the Calculation class?
Edit: I have used CompositeId in TransactionTotalMap as suggested by #David:
CompositeId().KeyProperty(x => x.Organisation.Id).KeyProperty(x => x.Charge.Id);
I'm still stuck on what to put in the CalculationMap though.
use the CompositeId() method in your mapping