Add custom columns on many to many relationship on NHibernate - asp.net

I have a movie entity and a actor entity, these 2 entities have a many to many relationship, so I've mapped it as ManyToMany(x=>x.Movies) and ManyToMany(x=>x.Actors) but i'd like to have the character the actor played on the movie, it should stay on the MoviesActorsPivot as a new column
But how can I do that using the Fluent Nhibernate Mapping in a way that I could get and save the data as easy as nhibernate does?
Not creating the pivot table manually and making the HasMany(x => x.MoviesActorsPivot) on both sides and managing the association by my own.
Edit:
Or if I map it creating the HasMany(x => x.MoviesActorsPivot) on both sides, how would i manage to insert and get al data like all movies from an actor or all actors that act on a movie, getting all the characters names?

The answer is:
NHibernate native many-to-many mapping does not support any additional setting on the pairing table
But, it could be replaced with a pairing object being first level citizen
public class MovieActor
{
public virtual Movie Movie { get; set; }
public virtual Actor Actor { get; set; }
... // more properties here
public virtual int Rating { get; set; }
}
public class Actor
{
public virtual IList<MovieActor> Movies { get; set; }
}
public class Movie
{
public virtual IList<MovieActor> Actors { get; set; }
}
That would be standard HasMany and References mapping. And the queriyng later will be more easier
Also check these:
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
nhibernate many to many with multiple table
many-to-many with extra columns nhibernate

Related

Store expanded entity in NoSql

In the book of adrian hall, there is a sample which fetches objects with sub objects.
In this case it looks like this:
public class JobDTO : EntityData
{
public string AgentId { get; set; }
public DateTimeOffset? StartTime { get; set; }
public DateTimeOffset? EndTime { get; set; }
public string Status { get; set; }
public string Description { get; set; }
public virtual CustomerDTO Customer { get; set; }
public virtual List<EquipmentDTO> Equipments { get; set; }
}
As you can see, there is one customer as complex data and multiple equipments.
On the client side, can we store complex data like this in the nosql offline store?
On the client side, can we store complex data like this in the nosql offline store?
According to your description, I have checked The Domain Manager from adrian hall's book. Also, I have tested the similar code, and the complex data could be stored in the SQLite offline store as follows:
For offline Sync, when pushing local Job table to the remote, the relationships Customer and Equipments need to be ignored by the server-side as follows:
// For incoming requests, ignore the relationships
cfg.CreateMap<JobDTO, Job>()
.ForMember(job => job.Customer, map => map.Ignore())
.ForMember(job => job.Equipments, map => map.Ignore());
As Existing Table Relationships with the MappedEntityDomainManager states for syncing the Job table:
Customer and Equipment data all comes down as one record. This has a side effect of ensuring that the Customer and Equipment data is read-only. You can only update the information in the Job table.
Also, as Adrian Hall mentioned that he prefers handling tables individually and handling relationship management on the mobile client manually. This causes more code on the mobile client but makes the server much simpler by avoiding most of the complexity of relationships.
Yes, why not.
Just follow this document to using off-line store
https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-xamarin-forms-get-started-offline-data

Why do I need HashSet in many-to-many relation?

I have found many tutorials that use HashSet ex
this.Supplier = new HashSet<supplier>();
In many-to-many relation. But som tutorials use the code below without HashSet (no more or less)
public partial class Product
{
public Product()
{
this.Supplier = new HashSet<supplier>();
}
public long ProductID { get; set; }
public string ProductName { get; set; }
//navigation property to Supplier
public virtual ICollection<supplier> Supplier { get; set; }
}
public partial class Supplier
{
public Supplier()
{
this.Product = new HashSet<product>();
}
public long SupplierID { get; set; }
public string SupplierName { get; set; }
// navigation property to Product
public virtual ICollection<product> Product { get; set; }
}
When I tested the code above and deleted
public xxxx()
{
this.xxxx = new HashSet<xxxx>();
}
I still got an association table and a many-to-many relation.
Why do I need HashSet?
Usually many-to-many relationship defined with ICollection in both table models:
public virtual ICollection<supplier> Supplier { get; set; }
public virtual ICollection<product> Product { get; set; }
The presence of ICollection on models means that lazy loading is enabled, allowing EF to create derived classes for them.
About the use of HashSet inside model generations, ChrisV said in HashSet in EF many to many:
HashSet implements a hash table that is very efficient for a lot of
operations, for instance searching a large set for a single item.
The usage of HashSet by default primarily based on efficiency reasons besides of non-null value, such like Yuval Itzchakov said in Entity Framework:Why the collection type of entity class need to be instanced in the default constructor?:
A HashSet is used because it guarantees that two values which are
equal to each other (which are equality checked by looking at their
GetHashCode and Equals methods) only appear once in the collection.
And yes, you can change the concrete type to any type which implements
ICollection<T>.
The explanations above can be summarized as "HashSet is initialization step of ICollection interface inside model's constructor which guarantees equality between each related model members". EF itself doesn't care what implementations should be apply on ICollection for table models, you can use List<T> in constructor replacing HashSet<T> and many-to-many relationship still doesn't affected.
Inside your template model (.tt file), you may see these lines to generate HashSet<T> by default as it implements ICollection:
foreach (var navigationProperty in collectionNavigationProperties)
{
#>
this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
<#
}
You may doing experiments by removing or changing HashSet<T> initialization when the model is re-generated, however I considered not a good practice to remove it for large amount of data operations.

ef two relations

I have the following Entity Framework 5 code first classes
public class Airplane
{
public int Id { get; set; }
public int LeftWingId { get; set; }
public virtual Wing LeftWing { get; set; }
public int RightWingId { get; set; }
public virtual Wing RightWing { get; set; }
}
public class Wing
{
public int Id { get; set; }
}
Airplane has one left and one right wing (both are required). Wing may belong to a 0..1 airplane (as a left or right wing) or to some other "flying device".
Deleting an airplane should cascade-delete it's wings.
How can this be configured in code-first fluent API?
Is it possible to have two 0..1 --- 1 associations in EF with cascade delete on both?
Unfortunately, you cannot have two associations pointing from same source table to same target table with cascade delete on both.
However, that is not due to limitation in EF, but in SQL server.
Quote from answer on another question you might want to check:
SQL Server does simple counting of cascade paths and, rather than
trying to work out whether any cycles actually exist, it assumes the
worst and refuses to create the referential actions (CASCADE): you can
and should still create the constraints without the referential
actions. If you can't alter your design (or doing so would compromise
things) then you should consider using triggers as a last resort.

EF Code First - Fluent API (WithRequiredDependent and WithRequiredPrincipal)

I have the following class:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public Couple Couple { get; set; }
}
public class Couple
{
public Guid Id { get; set; }
public User Groom { get; set; }
public User Bride { get; set; }
}
Important points:
Bride and Groom properties are required
One-to-one relationship
In the User class, it is Couple required
DbContext in OnModelCreating
modelBuilder.Entity<User>().HasRequired(u => u.Couple).WithRequiredPrincipal();
modelBuilder.Entity<Couple>().HasRequired(u => u.Bride).WithRequiredDependent();
modelBuilder.Entity<Couple>().HasRequired(u => u.Groom).WithRequiredDependent();
But I can not be required!
All fileds are with null in the database!.
How do I get the fields in the database as not null?
If possible using the API Flient.
It should be this :
modelBuilder.Entity<User>().HasRequired(u => u.Couple).WithRequiredDependent();
modelBuilder.Entity<Couple>().HasRequired(u => u.Bride).WithRequiredDependent();
modelBuilder.Entity<Couple>().HasRequired(u => u.Groom).WithRequiredDependent();
How WithRequiredDependent Works : Configures the relationship to be required:required without a navigation property on the other side of the relationship. The entity type being configured will be the dependent and contain a foreign key to the principal. The entity
type that the relationship targets will be the principal in the relationship.
Meaning : Let's consider your first line of code here. It creates a foreign key in the entity being configured (User) making it Dependant and making the other side of the relationship (Couple) Principal
Important : Don't you think the configuration you desire will generate a deadlock? I've not tested the code above, but this configuration seems to be a deadlock to me so i'm not sure if EF would allow you to create it. User must need a Couple, and Couple must need that same user i guess.

Entity Framework 4.1 Code First: Advice on persisting data from external source?

Part of my project is to persist data from another source. In this case we have an SAP data source that we will need to pull data from. I need to take the data from SAP and map it to entities I have in my application. Here is an example of an entity I have in my application:
public class Project : BaseEntity
{
public string Name { get; set; }
public string ProjectNumber { get; set; }
public string Description { get; set; }
public string CreatedBy { get; set; }
public string ModifiedBy { get; set; }
public string Currency { get; set; }
#region Navigation Properties
public virtual Address Address { get; set; }
public virtual CompanyCode CompanyCode { get; set; }
public virtual ICollection<Contact> TeamMembers { get; set; }
#endregion
}
As you can see, I have child objects that I map from SAP as well. I need some advice on the best way to insert and update my entities. I am struggling with knowing when to add (insert) entities to my context and when to attach (update) them, because SAP doesn't have knowledge of what my application may or may not have. I need to guard against duplicates, too. For example, should I perform a lookup of each child entity in my parent entity to see if they exist before I apply them to the parent? Then, add / attach the entire parent object to the context or handle each entity separately while still maintaing their relationships?
Yes you must manually test everything to make correct decision what must be inserted, updated or deleted. Depending on the application you can use some more complex queries to reduce number of round trips to the database - for example you can use single query with Contains to load all TeamMembers needed for processed Project or you can load Project with including all related data if you also need to test if project exists.
I did large synchronization application before and I end up with pre-loading all entities at the beginning with few queries and working completely in memory.
Don't forget to use DbSet's Local property or Find method to take advantage of already loaded entities.
You can also use some custom stored procedures to improve performance of this operation.

Resources