I searched a lot about how to map multiple inheritance or multiple interface implantation using EntityFramework or NHibernate But I didn't find anything useful.
I Simply want to map this structure using NHibernate:
public interface IA
{
string A { get; set; }
}
public interface IB
{
string B { get; set; }
}
public class C : IA, IB
{
string A { get; set; }
string B { get; set; }
}
As far as i know mapping this structure to a relational database means just to have foreign keys related with the interfaces primary keys, therefore the interfaces should have Keys like these:
public interface IA
{
Guid AId { get; set; }
string A { get; set; }
}
public interface IB
{
Guid BId { get; set; }
string B { get; set; }
}
public class C : IA, IB
{
public virtual Guid AId { get; set; }
public virtual Guid BId { get; set; }
public virtual string A { get; set; }
public virtual string B { get; set; }
}
But how to map this structure using NHibernate Or EntityFramework,and I don't know why multiple interface mapping is not mentioned in their documentation!
In NHibernate, you'll just map C as if the interfaces didn't exist.
You'll still be able to query on the interfaces, thanks to implicit polymorphism.
You will map it as any other class because this is not inheritance mapping. Moreover your code cannot be compiled because you must implement all properties in class C so you will get:
public interface IA
{
Guid AId { get; set; }
string A { get; set; }
}
public interface IB
{
Guid BId { get; set; }
string A { get; set; }
}
public class C : IA, IB
{
public virtual Guid AId { get; set; }
public virtual Guid BId { get; set; }
public virtual string A { get; set; }
}
Now your code can be compiled and you have class as any other. You will map AId and BId as composite key (depending on used ORM) and you are done. This is not inheritance because you have just single entity and no base enity. At least this is how it works with Entity framework.
As what I've founded it's not possible to have multiple inheritance in a relational database due to the concept and what Diego said is true in a "not interfaces get persisted scenario".
Related
I am working on Entity Framework Core Code First approach and ASP.Net Core 2.1 making 3 tables:
Person class
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public PeopleProfessions PeopleProfessions { get; set; }
}
Professions' class
public class Profession
{
public string Id { get; set; }
public string Name{ get; set; }
public PeopleProfessions PeopleProfessions { get; set; }
}
peopleprofessions' class
public class peopleprofessions
{
[ForeignKey("PersonId ")]
public string PersonId { get; set; }
public ICollection<Person> People { get; set; }
[ForeignKey("ProfessionId")]
public string ProfessionId{ get; set; }
public ICollection<Profession> Professions { get; set; }
}
On my Context:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<peopleprofessions>().HasKey(up => new { up.PersonId, up.ProfessionId });
}
Bearing this in mind:
People can have multiple professions.
The professions table is only for reading stored data like "Accountant".
I have doubts about how I can make table 3 only contain the foreigners and that it can meet the needs that I just mentioned.
I have tried to make the relationship appropriately but I also noticed that in tables 1 and 2 it requests both Id of the table people's professions.
I don't know if I am lost or if I am looking wrong or if there is an alternative to that situation. Thanks for any help you can give me.
You have the use of Collections on the navigation items a bit backwards. For your primary entities (Person and Profession), they should have collections, since it's one-to-many. But for the PeopleProfessions, each record is a single link to a specific entity, so no collection there just a direct object reference.
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PeopleProfessions> PeopleProfessions { get; set; }
}
public class Profession
{
public string Id { get; set; }
public string Name{ get; set; }
public ICollection<PeopleProfessions> PeopleProfessions { get; set; }
}
public class PeopleProfessions
{
public string PersonId { get; set; }
public Person Person { get; set; }
public string ProfessionId { get; set; }
public Profession Profession { get; set; }
}
You can, but don't need to specify a ForeignKey attribute because you are following EFs naming conventions(it will figure it out for you). Your OnModelCreating looks correct for the composite key.
You may want to consider removing the plural from PeopleProfessions (just call the class PeopleProfession) since one instance represents a single People-Profession relationship. I typically do this and but the navigation name in the entities remains plural, since it can represent more than one, i.e.
public ICollection<PeopleProfession> PeopleProfessions { get; set; }
As entity framework states, "Code first", here we go with the code first...
public class BaseModel
{
[Key]
public Guid Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
public BaseModel()
{
this.Id = Guid.NewGuid();
this.DateCreated = DateTime.Now;
this.DateChanged = DateTime.Now;
}
}
public class Association: BaseModel
{
public string Name { get; set; }
public string Type { get; set; }
public virtual List<Rule> Rules { get; set; }
public Association()
: base()
{
}
}
public class Rule: BaseModel
{
[ForeignKey("Association")]
public Guid AssociationId { get; set; }
//[Required]
public virtual Association Association { get; set; }
//[Required]
public string Name { get; set; }
public string Expression { get; set; }
public virtual List<Action> Actions { get; set; }
public Rule()
: base()
{
}
}
public class Action: BaseModel
{
public string Name { get; set; }
public string ActionType { get; set; }
[ForeignKey("Rule")]
public Guid RuleId { get; set; }
public virtual Rule Rule { get; set; }
public int Order { get; set; }
public Action()
: base()
{
}
}
So these are my four model classes that are using entity framework code first.
Each inherit from the baseclass, so they all have an Id Guid as Primary Key.
An Association has a list of rules. (Rule has FK to Association)
A Rule as has a list of actions. (Action has FK to Rule)
What I would like to do is only change and save the most upwards class = Association.
For example when deleting a rule, I would like this code to work:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
Association ass = this.DataContext.Associations.FirstOrDefault(a => a.Id == assId);
ass.Rules.RemoveAll(r => r.Id == ruleId);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}
On the context.savechanges this is giving me this error:
'The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.'
This error also occurs when deleting an action.
Is there a way to change the most upper (Association) object AND ONLY changing things to this Association.
I DO NOT want to say context.Rules.remove(...) or context.actions.remove(...)
here's the source: http://server.thomasgielissen.be/files/mvctesting.zip
you need VS2012, all nuget packages are included in zip and you should be able to build and run the project.
Thanks in advance for your feedback!
Greetz,
Thomas
I you want to fix this issue, you should store your relations through junction tables. I don't think that you can achieve what you need, with this model.
However if you put a junction table(or entity) between your entities, you can easily remove child objects and update parent object.
For example, put a junction entity between Association and Rule:
public class AssociationRule: BaseModel
{
public Guid AssociationId { get; set; }
public Guid RuleId { get; set; }
[ForeignKey("AssociationId")]
public virtual Association Association { get; set; }
[ForeignKey("RuleId")]
public virtual Rule Rule { get; set; }
public Association()
: base()
{
}
}
Now, you can easily remove any rule from any association:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
AssociationRule assr = this.DataContext
.AssociationRuless
.FirstOrDefault(ar => ar.AssociationId == assId && ar.RuleId == ruleId);
this.DataContext.AssociationRules.Remove(assr);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}
I'm trying to create a controller in MVC4 and I'm getting an error I don't understand (I'm new to MVC). It says "Unable to retrieve metadata for 'CIT.ViewModels.DashboardViewModel'..." and then gives 2 possible problems. One is that the DashboardViewModel has no key defined. The other is that EntitySet 'DashboardViewModels' has no key defined.
I defined a key for DashboardViewModel, but that didn't solve the problem. Here is my DashboardViewModel;
public class DashboardViewModel
{
public DashboardViewModel() { }
[Key]
public int Id { get; set; }
public Hardware Hardware { get; set; }
public Software Software { get; set; }
public HardwareType HardwareType { get; set; }
public Manufacturer Manufacturer { get; set; }
public SoftwarePublisher SoftwarePublisher { get; set; }
}
As you can see it is composed of classes. I did this so I could have multiple classes accessible from the same view. I didn't think it needed a key, but I added one and that didn't fix the problem. The other error sounded like it was looking for a DbSet for DashboardViewModels. As I understand it, your DbSets are your tables. I don't want or need a DashboardViewModels table. I'm only doing that so I can have multiple tables/classes accessible in my view. That's working fine up to this point.
When I am trying to create the controller, I am using the DashboardViewModel as as my model and Context as my context. Here is my context:
public class Context : DbContext
{
public DbSet<Software> Softwares { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<SoftwarePublisher> SoftwarePublishers { get; set; }
public DbSet<SoftwareType> SoftwareTypes { get; set; }
public DbSet<Hardware> Hardwares { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }
public DbSet<HardwareType> HardwareTypes { get; set; }
}
How do I address these errors?
I have a problem with NHibernate for a longtime which I solved by non-optimal ways/workarounds.
First of all, I'm using WCF REST to communicate with my client application. As you know, serializing persisted entities is not a best practise and always causes other problems. Thus, I always map my entities to DTO's with NHibernates Transformers. The problem is that I have entities which are more complex to use Transformers to convert them.
How can I map sub entities to sub dto's by using transformers or any other nhibernate feature?
Note: I don't want to use 3rd parties like Automapper.
These are the Entities and DTO's which I want to map. Variable names are exactly same with each other.
Entity Classes:
EntityType
public class crmEntityType : EntityModel<crmEntityType>
{
public crmEntityType()
{
Association = new List<crmEntityType>();
Fields = new List<crmCustomField>();
}
public virtual int ID { get; set; }
public virtual string Title { get; set; }
public virtual ICollection<crmEntityType> Associations { get; set; }
public virtual ICollection<crmCustomField> Fields { get; set; }
}
CustomFields
public class crmCustomField : EntityModel<crmCustomField>
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual crmEntityType EntityType { get; set; }
}
DTO's
EntityTypeDTO
[DataContract]
public class EntityTypeDTO
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public IList<CustomFieldDTO> Fields { get; set; }
[DataMember]
public int[] Associations { get; set; }
}
CustomFieldDTO
[DataContract]
public class CustomFieldDTO
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int EntityType { get; set; }
[DataMember]
public int FieldType { get; set; }
}
I found my solution by spending my day and night to work it out. Finally, I've got the best solution I could find. I hope it helps someone in my condition some day.
This linq query works with just one database round-trip. I think it maps the classes in memory.
return (from entityType in Provider.GetSession().Query<crmEntityType>()
.Fetch(x => x.Association)
.Fetch(x => x.Fields)
.AsEnumerable()
select new EntityTypeDTO()
{
ID = entityType.ID,
Title = entityType.Title,
Association = entityType.Association.Distinct()
.Select(asc => asc.ID).ToArray<int>(),
Fields = entityType.Fields.Distinct()
.Select(fi => new CustomFieldDTO
{ ID = fi.ID,
Name = fi.Name,
Value = fi.Value,
EntityType = fi.EntityType.ID,
Type = fi.Type
}).ToList()
}).ToList();
I have the following situation with fluent nhibernate:
public class Stuff
{
public Stuff()
{
Entities = new List<Entity>();
}
public virtual int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
}
public abstract class Entity
{
public virtual int Id { get; set; }
public virtual string Type { get; set; }
public virtual Stuff Stuff { get; set; }
}
public class Person : Entity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public class Animal : Entity
{
public virtual string Species { get; set; }
}
And then, i have the following code to use automap and generate these mappings:
var sessionFactory =
Fluently.Configure().Database(persistenceConfigurer).Mappings(
m =>
m.AutoMappings.Add(
AutoMap.Source(new Types(typeof(Entity), typeof(Person), typeof(Animal), typeof(Stuff))))
.ExportTo(#"e:\")).ExposeConfiguration(BuildSchema).BuildSessionFactory();
however, what's happening is that i get the following exception:
---> NHibernate.MappingException: Association references unmapped class: ConsoleApplication1.Models.Entity
if i make the entity class non abstract this works, however, i'd like to avoid having that table in the database but still maintain the hierarchy concept with the re
You need to add your auto mappings like this
AutoMap.AssemblyOf<Entity>(yourConfiguration).IgnoreBase<Entity>();
Not only will this ignore your Entity base class but you don't need to add each entity manually so long as each model inherits from Entity.