one to many relationship entity framework required in two side - c#

how to make relation between two tables one to many and required in two sides
for example
i have project and image table
every image has project And every project has at least one image
i make this
every image has project
public class ImageMap : EntityTypeConfiguration<Image>
{
public ImageMap()
{
this.ToTable("ProjectImage");
this.HasKey<int>(i => i.Id);
this.Property(i => i.ImagePath).IsRequired().HasMaxLength(2000);
this.HasRequired<Project>(i => i.Project).WithMany(p => p.Images)
.HasForeignKey(i => i.ProjectId);
}
}
how make every project has at least one image or many ??? (Entity Framwork version 6)

You can't ensure a "at least one" condition in the "many" part of a one to many relationship. This is not something you can manage in a db, without a check constraint, for example (you could also do a validation in your application to manage that).
One way to achieve that could be to create a one-to-one relationship AND a one-to-many, both pointing on the same table / entity.
So your Project would have a MandatoryImage property, for example, and a AlternativeImages (collection) property.

I think that is something you should control in your Business Logic or Data Access layer before call SaveChanges method of your context. As far I know there is no way to configure that using Fluent API. So you could do something like this:
if(project.Images.Count()>0)// Or project.Images.Any()
{
context.SaveChanges();
}

Related

Entity Framework 6 Code First - Comments on a Tree Structure

I'm attempting to implement heterogeneous association in my Data Model (Entity Framework 6, Code-First approach).
I have an existing structure of classes, let us call them Tree, Branch and Leaf. A Tree may have many Branch objects, and a Branch may hold many Leaf objects. The relationships between the three levels have a cascade-delete behavior (delete a branch and you also delete the leaves, etc.).
Now I'm trying to let users add a comment-like object on each of those levels. I had a few problems related to data-modelling, as I want each of the 3 entity types to be able to have many comments and each comment to belong to one and only one entry. I'd also like for all comments to be in the same table. I've tried two different approaches:
Alt. 1
Implement inheritance so that the Comment (abstract) can be a TreeComment, BranchComment or LeafComment, following the Table per Hierarchy (TPH) approach (as seen, for example, here) of having an abstract class (Comment) for comments and then derive it to TreeComment, BranchComment, etc. That is achieved by coding the models like this:
public abstract class Comment
{
// ID
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
}
public class TreeComment: Comment
{
// Foreign Keys
public Guid TreeID { get; set; }
// Navigation Properties
public virtual Tree Tree { get; set; }
}
(... BranchComment and LeafComment ...)
(... add virtual ICollection<TreeComment> to Tree, virtual ICollection<BranchComment> to Branch, etc.)
...which can be expressed with this diagram:
The problem with this approach is that the relationship between the Comment table and the other 3 doesn't have ON DELETE CASCADE or ON DELETE SET NULL set. If I try to change that to more than one table, I get a:
Introducing FOREIGN KEY constraint 'FK_Comment_Branch_BranchID' on
table 'Comment' may cause cycles or multiple cascade paths. Specify ON
DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
I understand that this is because SQL Server "doesn't know" that only one of the FK's in the Comment table is supposed to be used at any time.
Alt. 2
Generalize the Tree/Branch/Leaf trio into a CommentableEntity using the Table per Type (TPT) approach and connect the Comment table to that abstract one. This can be achieved by implementing inheritance in the model classes (just like I did before) and adding the annotations [Table("Tree")], [Table("Branch")] and [Table("Leaf")] to each of the subclasses to make sure we get a table for each (and not a single table like in TPH). The Model, then looks like this:
This approach has two problems:
Deleting a concrete object (e.g. a branch) will not delete the base entry in the abstract table, leaving "garbage" (abstract entities and their comments) behind.
The FK relationship between the abstract and concrete classes lacks a cascade delete. So I can't really delete the base object. If I try to add one I get another complaint on how introducing such rule would cause cycles of multiple cascade paths.
I've also tried using DB triggers (CREATE TRIGGER ... INSTEAD OF DELETE...) on both approaches but they seem to be a big no-no as EF can't track the changes done by them.
This is frustrating and I'm sure this (comments on a tree structure) is a very typical scenario in Web development; but I can't seem to find a way to allow it. I'm looking for all advice I can get on how to effectively model these relationships (EF 6 Code First) without placing too much weight on the Business Logic layer.
EDIT:
I believe this is what user #Deepak Sharma mentioned in his comment: TPH inheritance in the node classes. If so, this also doesn't work for the same reason: cycles of multiple cascade paths.
Ok, so here's how I'm currently solving the problem:
I chose the second alternative - generalize the Tree/Branch/Leaf trio (let's call these "nodes" for simplification) into a CommentableEntity (the base class) using the TPT approach - as seen above. I end up with one table for each of the three node classes + one base class that holds the relationship to a Comment table.
Then, in my InitializeDatabase(MuDbContext context), I added one Stored Procedure and Trigger for each of the three tables to the database using the context.Database.ExecuteSqlCommand() method.
1) The Stored Procedure has to be mapped in EF like this:
this.MapToStoredProcedures(s => s.Delete(d => d.HasName("TriggerName").Parameter(b => b.ID, "parameter_name")));
... for each of the three models and basically is a replacement for the default delete. In my case, I wrote it so that first it deletes the actual node in its table (Tree/Branch/Leaf) and then the corresponding base object (CommentableEntity).
2) The Trigger fires after a node is deleted and makes sure that the corresponding base object is also deleted.
In case you're wondering why do I have such redundancy (a Trigger and a Stored Proc. that do almost the same thing) it's because whenever a node is deleted (say, a tree), EF calls its Stored Proc. in order to delete it. Then, the nested nodes (the tree's branches) are deleted via the DB's cascade-delete, which doesn't delete the base objects, and not via the Stored Proc.. Thus, the Trigger. On the other hand, if I only had the trigger (no Stored Proc.), EF would freak out after the deletion because it wouldn't be able to track its changes.
I could, of course, just change each of the Stored Proc. for each of the tables so that they also delete all of the nested objects as well and remove the cascade-delete setting. But the current solution seems to be working and good enough for me.
I will test this out and delete this answer if I find out that this doesn't actually work. If you see any disadvantages in this approach (and know how to avoid them) please leave a comment.
Possibly the answer is to augment Alt 1 by declaring some rules within the OnModelCreating Method. Also this assumes that the the Tree, Branch and Leaf classes have a Comments collection against them.
Within your DbContext you could do the following ...
public class YourDbContext : DbContext
{
... your DbSet properties
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tree>()
.HasMany<TreeComment>(o => o.Comments)
.WithRequired(com => com.Tree)
.HasForeignKey(com => ds.TreeID)
.WillCascadeOnDelete(false);
}
}

Entity Framework - Manually Add Properties to Many-to-Many Relationship - Model First

Scenario:
As mentioned here, once you add additional properties to a simple join table (many-to-many relationship), it's no longer a hidden association. These questions also address this:
Many-to-Many relationship in Entity Framework with relationship informantion
How can I add properties to an association (relationship) using the Entity Framework
The existing code already uses the simple, automatically hidden navigation properties, and there are some minor customizations to the autogenerated tables, and so I'd like to avoid refactoring the entire project when I alter the underlying relationship table.
Question:
Is there a way so that both the automatic navigation (many-to-many) accessors can remain, but I can also access the relationship entity directly?
I could just write my own accessors selecting from the relationship table, but then they're no longer EntityCollections and thus I'm concerned that I lose whatever magic happens under the hood like tracking, etc.
Can I manually add EntityCollections to entities?
Expectation:
Originally: Product* <-> *Offer
a Product has many Offers (like 50% off, BOGO)
the same Offer could apply to many Products ("Red Shirt" and "Blue Pants" are BOGO)
Desired: Product* <-[sort]-> *Offer
When I list Offers for a Product, I can sort them independently
i.e. "Red Shirt" has "50% off" then "BOGO", but "Blue Pants" shows "BOGO" then "50% off"
then I would want to be able to do:
// original access, do stuff
List<Offer> applicableOffers = currentProduct.Offers.Where(...);
// hit up the join table directly for properties
var applicableOffersInOrder = applicableOffers.OrderBy(o => o.ProductOffers.Sort);
rather than
var applicableOffersInOrder = currentProduct.ProductOffers
.OrderBy(o => o.Sort)
.Offers.Where(...);
I think the easiest way to do it is simply add two properties manually in a non-autogenerated partial class:
partial class Offer
{
public IQueryable<Product> Products
{
get { return this.ProductOffers.Select(x => x.Product); }
}
}
partial class Product
{
public IQueryable<Offer> Offers
{
get { return this.ProductOffers.OrderBy(x => x.Sort).Select(x => x.Offer); }
}
}
This won't help for when you want to add a new ProductOffer, but since you actually have extra data (Sort) you should be doing that via the ProductOffers collection anyway.

Entity Framework updating two databases

As I've mentioned in a couple other questions, I'm currently trying to replace a home-grown ORM with the Entity Framework, now that our database can support it.
Currently, we have certain objects set up such that they are mapped to a table in our internal database and a table in the database that runs our website (which is not even in the same state, let alone on the same server). So, for example:
Part p = new Part(12345);
p.Name = "Renamed part";
p.Update();
will update both the internal and the web databases simultaneously to reflect that the part with ID 12345 is now named "Renamed part". This logic only needs to go one direction (internal -> web) for the time being. We access the web database through a LINQ-to-SQL DBML and its objects.
I think my question has two parts, although it's possible I'm not asking the right question in the first place.
Is there any kind of "OnUpdate()" event/method that I can use to trigger validation of "Should this be pushed to the web?" and then do the pushing? If there isn't anything by default, is there any other way I can insert logic between .SaveChanges() and when it hits the database?
Is there any way that I can specify for each object which DBML object it maps to, and for each EF auto-generated property which property on the L2S object to map to? The names often match up, but not always so I can't rely on that. Alternatively, can I modify the L2S objects in a generic way so that they can populate themselves from the EF object?
Sounds like a job for Sql Server replication.
You don't need to inter-connect the two together as it seems you're saying with question 2.
Just have the two separate databases with their own EF or L2S models and abstract them away using repositories with domain objects.
This is the solution I ended up going with. Note that the implementation of IAdvantageWebTable is inherited from the existing base class, so nothing special needed to be done for EF-based classes, once the T4 template was modified to inherit correctly.
public partial class EntityContext
{
public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
var modified = this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added); // Get the list of things to update
var result = base.SaveChanges(options); // Call the base SaveChanges, which clears that list.
using (var context = new WebDataContext()) // This is the second database context.
{
foreach (var obj in modified)
{
var table = obj.Entity as IAdvantageWebTable;
if (table != null)
{
table.UpdateWeb(context); // This is IAdvantageWebTable.UpdateWeb(), which calls all the existing logic I've had in place for years.
}
}
context.SubmitChanges();
}
return result;
}
}

Map Multiple Tables to One Table Produces Foreign Key Column for each in Fluent NHibernate

I want to Point 3 tables Property, Business, Automobile to single Table named Utility.
Using Fluent NHibernate, I followed
public UtilityMap()
{
// some Mappings
References<Automobile>(x => x.Automobile, "LeaseRefId").Cascade.None();
References<Business>(x => x.Business, "LeaseRefId").Cascade.None();
References<Property>(x => x.Property, "LeaseRefId").Cascade.None();
}
and in Each table that maps to the Utility, I followed
public AutomobileMap()
{
//Some Mappings
HasOne<Utility>(x => x.CommonDatas)
.Cascade.All();
}
"I want to have a single column in Utility Table that can store the
references of all the three (Automobile, Business, Property) tables.
But fluent Nhibernate is creating foreing key columns for each of the
table referenced. I want to avoid this."
Is there any way to achieve this??
Please suggest
Thanks in Advance.
For this to work as you expect, you'll need to have a base class for Automobile, Business and Property classes. Let's call it LeaseSubject. Presumably, you already have something like that in your domain model.
Your Utility class should have only one property, instead of three you have now:
public virtual LeaseSubject LeaseSubject { get; set; }
This way you not only have easier mapping, but also promote the maintainability of your code. What if at some later time you decide to have one more lease subject, i.e. Yacht? Instead of adding another Yacht property to your Utility class, you just need to inherit from LeaseSubject, and it could be contained within the LeaseSubject property on Utility class.
Now you could map your Utility simply as:
public UtilityMap()
{
// Other mappings...
References(x => x.LeaseSubject, "LeaseRefId");
}
For mapping Automobile, Business and Property classes, you would use one of three available NHibernate inheritance strategies:
table per class hierarchy
table per subclass
table per concrete class
For FluentNHibernate inheritance mapping, take a look here.
Since you already have separate tables, I believe the correct strategy for your case would be Table per concrete class. You can find some more info in this article.

Many to Many (self related) specific order entity framework

Hello I'm trying to do the impossible apparently.
I need a self referenced table with a many to many relationship to itself that also has a specific order in c# entity framework (4.2) database first.
Think of it like Friends having Friends in which they order their friendship > Best Friend to Worst Friend.
Is there anyway to do this without using the "FriendToFriend" relationship entity? I would like to be able to use Friend.Friends (removing the order column creates it), but I would have a default order based on their friendshipOrder. My work around is looking like extending the generated classes to have a new property for Friends in order.
Any one else have any better ideas?
Entity framework does not support ordered collections. This is one of many situations where EF shows its immaturity.
Try nHibernate if it is a viable option. It supports ordered collections.
With EF you will have to map the intermediate table with extra column and manually adjust the ordering according to your logic.
I know I'm late to this, but when designing this as a data model, I would prefer to add a relationship table, and that relationship table should have a property that defines the order (for example, worst friend is 0, best is 100).
Then, in EF, I would explicitly order by that property, if the list I'm retrieving should be of that order.
That means that whatever method you use to query the data, that relationship can be consistently used. So if you were using EF, you could use it (although, yes, it's not as handy as Friend.Friends, but the code would be clearer as to its intention - Friend.FriendRelationships.Select(p => p.Friend).OrderBy(p => p.OrderValue)), and if you were using direct SQL, then you could use it too.
If I came across Friend.Friends in code, I would have no idea what ordering would be applied to it.
If you must have it though, you could always add it as a non-db property -
public class Friend
{
public virtual List<FriendRelationship> UnorderedFriendList { get; set; }
[NotMapped]
public IEnumerable<Friend> Friends
{
get
{
return UnorderedFriendList.Select(p => p.Friend).OrderByDescending(p => p.OrderValue);
}
}
}

Categories

Resources