is there a way to do a reverse lookup in nhibernate? - c#

i have two tables:
Components
ComponentDependencies
ComponentDependencies has two columns: ComponentId and ComponentDependencyID. (both foreign keys into Id field of Component table.
i am using fluent nhiberate and i have my component object having:
public virtual IList<ComponentDependency> Dependencies { get; set; }
and in my ComponentMap class, i have nhibernate map:
HasMany(x => x.Dependencies)
.AsBag().Inverse();
So when i have a component object, i can see a list of its dependencies.
Is there anyway i can have an additional property have the "reverse" list. What i mean is that i want a property called "DependentOf" which is also a
IList<ComponentDependency>
which has all of the items where this current component is the dependent component in the relationship?

This looks like a bill-of-materials problem with Components having a many-to-many relationship to itself through the ComponentDependencies linking table. You can map both relationship directions by alternating which column is the parent key column:
HasManyToMany(x => x.Dependencies).Table("ComponentDependencies")
.ParentKeyColumn("ComponentId").ChildKeyColumn("ComponentDependencyId");
HasManyToMany(x => x.DependentOf).Table("ComponentDependencies")
.ParentKeyColumn("ComponentDependencyId").ChildKeyColumn("ComponentId");

Related

Entity Framework Inheritance and Relationships

I'm trying to implement TPH inheritance with Entity Framework 6 Code First and am having problems with a relationship from my inherited types.
My code is
public abstract class Base...
public class Inherited1 : Base
{
public virtual Type1 Rel { get; set; }
...
public class Inherited2 : Base
{
public virtual Type1 Rel {get;set;}
...
So the inherited types have the "same" relationship. The inheritance itself works fine, but the problem I'm having is that the relationship to the table Type1 will be added twice (logical...) and the other relationship is from Inherited2.Id to Type1.Id instead of Inherited2.Type1Id to Type1.Id that the first relationship is (correctly).
I'm not sure if I made any sense explaining this and with the partial code sample with changed type names, but I hope you got the point. Ask for more details if you need any.
I probably could implement this correctly with
UPDATE
I've created a sample Github repo to demonstrate the issue. Feel free to tell me what I'm doing wrong. https://github.com/antsim/EntityFrameworkTester
Try to use the following
1- if you want TPT
modelBuilder.Entity<Inherited1>()
.ToTable("Inherited1s")
.HasKey(x => x.YourKey)
.HasRequired(x=>Type1)
.WithMany()
.HasForeignKey(x=>Type1Id)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Inherited2>()
.ToTable("Inherited2s")
.HasKey(x => x.YourKey)
.HasRequired(x=>Type1)
.WithMany()
.HasForeignKey(x=>Type1Id)
.WillCascadeOnDelete(false);
2 - if you want TPH
modelBuilder.Entity<Base>()
.ToTable("YourTableName")
.HasRequired(m=>m.Type1)
.WithMany()
.HasForeignKey(m=>m.Type1Id)
.WillCascadeOnDelete(); // true or false as you want
for more details you might check this article
based on the sample you provided
Attachment and Document are inherited from File and you are using TPH which means One table will be created with a Discriminator field.
Document and FileContainer has a relation of type 0..1 which means a Foreign Key FileContainerId should be created in the Document hence in the File table
FileContainer and Attachment has a relation of type 0..n, then another nullable foreign key will be created in the table File
in the example you provided, I made the following changes
Add FileContainerId to the table Document
Add FileContainerAttachmentId to the table Attachment
The changes made on the TestContext was
modelBuilder.Entity<FileContainer>()
.HasOptional(x => x.Document)
.WithMany()
.HasForeignKey(t => t.DocumentId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Document>()
.HasRequired(t => t.FileContainer)
.WithMany()
.HasForeignKey(t => t.FileContainerId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Attachment>()
.HasRequired(t => t.FileContainer)
.WithMany()
.HasForeignKey(t => t.FileContainerAttachmentId)
.WillCascadeOnDelete(false);
the output was correct ( File table contains discriminator field in addition to two relations one for the document with the container and the other for the attachment with the container).
A better solution in my opinion is:
To add a class FileType ( Id, Name) with values Attachment, Document, and add it as a foreign key in File
To add only one relation 0..n between the FileContainer and File
To validate that only one record in the File of type document to same container
Hope this will help you

Many to Many relation in EF made without links to the entity

I would like to shift from the common pattern in many to may relation using implicit joint table and instead I want to use the joint table explicitely and implement a kind of lazy load of many-to-many dependent entity.
This is framed by the design of disconnected sets, where the objects graph is passed via http to the application server. On the server I update just keys, not entities.
The reason for that: if I try to save the same entity defined with different instances of an entity model, I get the concurrent scenario error.
Instead, I don't want to pass on object, rather I just want to pass the joint table with IDs.
Class
-------
int ID
List ClassStudent
Student
-------
int ID
List ClassStudent
ClassStudent // this type must be explicitely defined in the model
--------------
StudentID
ClassID
Is it possible to define such configuration with FluentApi?
The common scenarion is using types:
Student
---------------
int ID
List Classes
Class
----------
int ID
List Students
I don't need this beacuse of concurrent changes.
Need to help with fluent API. I've tried many cases.
Yes, it's possible. All you need is to configure a composite PK for the ClassStudent and two unidirectional one-to-many relationships (Student -> ClassStudent and Class -> ClassStudent).
Something like this:
modelBuilder.Entity<ClassStudent>()
.HasKey(e => new { e.StudentID, e.ClassID });
modelBuilder.Entity<Student>()
.HasMany(e => e.ClassStudent)
.WithRequired()
.HasForeignKey(e => e.StudentID);
modelBuilder.Entity<Class>()
.HasMany(e => e.ClassStudent)
.WithRequired()
.HasForeignKey(e => e.ClassID);

How can do I use fluent nhibernate to map this relationship?

I have the following database tables:
Project table
Dependency table with columns (Id, ProjectId, DependentProjectId)
on my Project domain object i have the following mapping to be able to take a project and read its dependencies
public virtual IList<Dependency> Dependencies { get; set; }
HasMany(x => x.Dependencies).AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);
I now want to create another property on the Project object to read all of the items where it is the dependent project (to find out the list of projects that has "me" as a dependency.
what is the correct way to do that mapping in fluent nhibernate?
I'd say you would just have to specify the .KeyColumn for the one to many relation, e.g.
HasMany(x => x.Dependencies)....KeyColumn("ProjectId")
HasMany(x => x.DependentProjects)....KeyColumn("DependentProjectId")

What's the proper design for a one-to-one relationship using Fluent NHibernate?

I have a table of Recipes. Each Recipe has one and only one row in table RecipeMetadata, which contains various data about the recipe that I don't want to store in the Recipes table for various reasons. Thus, Recipes and RecipeMetadata have a one-to-one mapping. My Recipes table is as follows:
public partial class RecipesMap : ClassMap<Recipes>
{
public RecipesMap()
{
Id(x => x.RecipeId);
// Map() various columns here
HasMany(x => x.Ingredients).KeyColumn("RecipeId");
HasOne(x => x.Metadata);
}
}
And here's my RecipeMetadata table:
public partial class RecipeMetadataMap : ClassMap<RecipeMetadata>
{
public RecipeMetadataMap()
{
Id(x => x.RecipeMetadataId);
// Map() various columns here
References<Recipes>(x => x.Recipe).Column("RecipeId").Not.Nullable();
}
}
However, when I load a Recipe and access the Metadata property, it attempts to find a row in RecipeMetadata where Recipes.RecipeId = RecipeMetadata.RecipeMetadataId. In other words, it does the join using the primary keys on both tables.
With my table schema, RecipeMetadataId is a key unique only to that table, and has nothing to do with RecipeId. RecipeMetadata has another column, also called RecipeId which has a foreign key constraint on `Recipes. The JOIN should work as:
Recipes.RecipeId = RecipeMetadata.RecipeId
My Questions:
Am I wrong for wanting RecipeMetadata to have its own unique ID, and to use a separate column to link this to Recipes? Obviously, I have a FK constraint as well as a unique index on RecipeMetadata.RecipeId so there's no perf impact. Yes, there's some extra bytes on the disk for storing an arguably unnecessary ID on this table.
I've never seen a table whose primary key also has a foreign key constraint on another table. Is this legit practice? It seems to be the way nHibernate prefers to behave by default. Should I give in and let it have its way?
Provided I don't want to change the database (Though I can be convinced to do so if given a legitimate reason), how can I create the desired one-to-one mapping with this model?
NHibernate has a strict definition of one-to-one relationships. Strict but fair. In NHibernate one-to-one relationship means that the a row in table A always has a matching row in table B.
Right or wrong, that won't work with NHibernate's one-to-one mapping. Note that the model you propose is identical to how a one-to-many relationship would be modeled.
It's legit and enforces the one-to-one relationship.
Since you want the recipe to always have an associated metadata row, I would model it using NHibernate's one-to-one mapping. Alternatively, you can map it as one-to-many but only expose one instance as a property.
See also: Ayende's post on the topic.

Entity Framework Code First: Custom Mapping

public class User
{
public int Id {get;set;}
public string Name {get;set}
public ICollection<User> Followers {get;set;}
public ICollection<User> Following {get;set;}
}
My Model looks like above, Entity framework automatically creates A table and UserUser with rows User_ID and User_ID1 in DB to map this model. I want to map that table and rows myself.
How can i do that, Thanx!!
From Scott Gu's blog about Many-valued Associations:
Many-to-Many Associations
The association between Category and Item is a many-to-many
association, as can be seen in the above class diagram. a many-to-many
association mapping hides the intermediate association table from the
application, so you don’t end up with an unwanted entity in your
domain model. That said, In a real system, you may not have a
many-to-many association since my experience is that there is almost
always other information that must be attached to each link between
associated instances (such as the date and time when an item was added
to a category) and that the best way to represent this information is
via an intermediate association class (In EF, you can map the
association class as an entity and map two one-to-many associations
for either side.).
In a many-to-many relationship, the join table (or link table, as some
developers call it) has two columns: the foreign keys of the Category
and Item tables. The primary key is a composite of both columns. In EF
Code First, many-to-many associations mappings can be customized with
a fluent API code like this:
class ItemConfiguration : EntityTypeConfiguration<Item> {
internal ItemConfiguration()
{
this.HasMany(i => i.Categories)
.WithMany(c => c.Items)
.Map(mc =>
{
mc.MapLeftKey("ItemId");
mc.MapRightKey("CategoryId");
mc.ToTable("ItemCategory");
});
} }
Register this configuration in your DbContext's (you using the DbContext api right?) like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ItemConfiguration());
}
Good luck, hope this help!
To map an entity to itself, you would do something like this
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Followers)
.WithMany().ForeignKey(u => u.FollowerId);
base.OnModelCreating(modelBuilder);
}
its hard to tell without seeing your database model though, and how you actually relate the followers to the user.

Categories

Resources