I am creating a new database using EF code-first which contain the following classes:
Address, Contact, Account, Customer (a sub-class of Account), and SalesOrder.
The Address class is the one giving me problems at the moment, it can have no foreign key because it can be linked to any of the other five classes (with more to come), and each of the other classes can have one or more navigation properties pointing back to it.
The navigation properties should look as follows:
Contact.AddressId?
Contact.Address
Account.AddressId?
Account.Address
Customer.DeliveryAddresses
SalesOrder.InvoiceAddressId
SalesOrder.InvoiceAddress
SalesOrder.DeliveryAddressId?
SalesOrder.DeliveryAddress
It should be possible for these classes to share the same Address record, e.g. an Account has an Address, this can also be linked to a SalesOrder, a different Address, linked to the Customer, could be linked to another SalesOrder. All Addresses linked to Accounts and Customers should be unique, but other classes should be able to share links to these Addresses.
I have tried setting it up with all the possible fluent configurations I can think of, with my DbContext having a DbSet property and without (ultimately I don't think it should have it's own DbSet property, as the Addresses should only be accessible from the various root objects, but if that's the only way to get it to work I'm happy to manage the inserts/deletes myself).
I tried making all the navigation properties nullable (ideally SalesOrder.InvoiceAddressId should not be nullable), and also had to remove the Customer.DeliveryAddresses Many-to-Many mapping at one point because that was confusing the issue.
I get various errors depending on how I have it set up, either Multiplicity conflicts due to non-nullable fields, or Cascade on Delete errors when I have no DbSet property and I try and let EF handle the inserts and deletes.
I also end up with unwanted null rows when I do have a DbSet property set. e.g:
add three Address records to the DbSet (Address(1), Address(2), Address(3),
add two Accounts to the DbSet (Account(1) & Account(2)),
add multiple SalesOrders,
set Account(1).AddressId = 1
set Account(2).AddressId = 2,
set SalesOrder(n).InvoiceAddressId = 1,
set SalesOrder(n).DeliveryAddressId = 3
This will correctly create the Address records, but the related keys will only be set correctly if the various Id foreign-key properties are used, rather than the navigation property, and even if the Id properties are used the foreign keys all look correct, but orphaned records for each SalesOrder (or two per order if both navigation properties are used) end up in my Address table with all their fields bar Id set to NULL.
The only thing I can think of that I haven't tried would be to create multiple sub-classes of Address and use each one with it's related class (e.g. SalesOrderDeliveryAddress), but that doesn't seem ideal. I'd rather not do that unless I have to.
Is what I'm looking for possible to set up in EF, or is there some other way to go about doing it?
Thanks,
David
There are several issues making this confusing. To start with I would switch off the default cascade on delete to get rid of multiple cascade paths and come back to that later.
Then read about adding disconnected trees, foreign keys and navigation properties here: http://msdn.microsoft.com/en-us/magazine/dn166926.aspx
Then I would set up the entities you way you want them and repost a more specific issue. (You have tried lots of stuff so it's hard to work out what happens when here)
Once you've got adding and updating working you can come back and work out where you can put in cascade delete and where it needs to be manual
Related
I have a custom activity, and a customer want to have "RegardingObjectId" reference to a Account or Contact.
But also want a reference to a Incident.
Is this possible? And if so, what properies/methods are I looking for.
You can set activities in CRM regarding any entity that has been enabled for activities. Out of the box Account, Contact, and Case (the display name for Incident) are all among the activity enabled entities.
It does not matter that we are talking about a custom activity: Any activity can be set regarding any activity enabled entity.
You should thus already be able to set RegardingObjectId to either Account, Contact, or Case (though only one at a time).
As I understand it, you want to have a lookup to either Account or Contact while on the same CustomActivity also having a reference to a Case. It is not possible to have multiple regarding-style fields. Instead, you can add a normal 1:N-relationship from Case to your CustomActivity and add that as a lookup field on your CustomActivity form. In that way the regarding-field would be used for Account/Contact, while the lookup would be used for Case.
The way I've approached this is that the Regarding object is the primary reference. I then set up auxiliary lookups for the additional relationships. So for example:
Custom activity is in regards to this contact
It is also related to a custom entity, so a lookup is there and set for the custom entity.
It is also related to a case, so a separate 1:N relationship to Case is established with a lookup that is set to that Case.
The added benefit here is that the user gets to decide what the additional relationships are. Then when you set up the grids on the custom entity and case (for my example) you set the relationship to the additional lookups and not the regarding.
I have a repository in EF that needs to Update detached entities but ones which exist in the ObjectStateManager. Imagine you have a session open on a DBContext where you have loaded a particular entity, but then you receive a request to update from a detached version of the object. The only way I have found to do this is to GET the existing object in the state manager, then one by one, update the fields to the fields of the passed-in object. Then set the state of the object state manager version to modified, and save the context.
This works for simple entities that don't contain navigation properties.
I am now trying to do so on an entity that has a many-to-many relationship.
Imagine you have a BlogPost object, and a Hashtag object. This is a many to many relationship. I have defined this and in the database I can see I have three tables, the BlogPost, the HashTag and the mapping table.
What I want to be able to do is edit the blog post on the front end, pass in the updated blog post with it's new list of hashtags that apply to it, and update the database.
The problem is the list of hashtags could be completely unrelated to the old one, so I first have to clear out all the previous mappings, then add the new ones in. If they are the same, this will be a necessary redundancy but the only way to achieve it.
I cannot figure out how to clear out the previous mappings in the many to many relationship though. I have tried
foreach (var tag in dbBlogPost.Hashtags)
dbItem.Hashtags.Remove(tag);
I then add the new hashtags to the empty collection, then do
Work.Context.Entry(dbItem).State = EntityState.Modified;
Work.Context.Save();
But when I save the repository, I get the following exception
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.
Can anyone suggest what I am doing wrong?
The DbSet<TEntity>.Local represents a local view of all Added, Unchanged, and Modified entities in this set. So you can clear all the collection before make any change.
dbBlogPost.Hashtags.Local.Clear();
I have a frustrating situation owing to this little quirk of EF. Here's a simple demo of the behavior. First the DB schema:
As you see, RestrictedProduct is a special case of product, which I'm intending to make a subclass of Product with some special code.
Now I import to an EF data model:
Oops! EF saw that RestrictedProduct had only 2 fields, both FKs, so it mapped it as a one-to-many relationship between Product and Restriction. So I go back to the database and add a Dummy field to RestrictedProduct, and now my EF model looks much better:
But that Dummy field is silly and pointless. Maybe I could delete it? I blow away the field from the DB table and the entity model, then refresh the model from the DB...
Oh, no! The Product-Restriction association is back, under a new name (RestrictedProduct1)! Plus, it won't compile:
Error 3034: Problem in mapping fragments starting at lines (x, y) :Two entities with possibly different keys are mapped to the same row. Ensure these two mapping fragments map both ends of the AssociationSet to the corresponding columns.
Is there any way to prevent this behavior, short of keeping the Dummy field on the RestrictedProduct table?
I just came across the same issue, and as an alternative to putting the dummy field in your RestrictedProduct table to force the creation of an entity you can also make your RestrictedProduct.RestrictionId field nullable and EF will then generate an entity for it. You can then modify it to use inheritance and any subsequent "Update model from database" will not cause undesired nav properties. Not really a nice solution but a work around.
Let's walk slowly into your problem.
1st thing you need to decide is if the restricted product is
really a special case of product or is it a possible extension
to each product.
From your original DB Scheme it seems that any product may have
a relation to a single restriction however a single restriction
can be shared among many products.. so this is a simple 1 to many
situation which means that restricted product is NOT a special case
of product! Restriction is an independent entity which has nothing
to do with product in a specific way.
Therefore EF is correct in the 1st importation of your scheme:
1. a product can have 0 or 1 restrictions.
2. a restriction is another entity which can be related to many products.
I do not see your problem.
I have couple of questions with update functionaliy using NHibernate
I have Customer and location entities with 1:n relationship. Customer has location property. While creating/updating customer entity, I just assigned location property and commited changes.
new Location() { Id = ViewModel.LocationId };
Is it proper way to do it or do I need to retrieve the location entity from db and attach it again like below
newCust.Location = GetlocationfromDB(ViewModel.LocationId);
And how does it work with m:n relationship. I have order and orderitems entities. So, if a newgroup is added/deleted, do I need to check which group is added and get from db and attach it or just groupid will do fine..
This isn't the right way to do it - it might work if you have your unsaved-value mapping right for the primary key, but the proper way to do it is to use session.Load(ViewModel.LocationId) see http://ayende.com/blog/3988/nhibernate-the-difference-between-get-load-and-querying-by-id
There are a number of ways of dealing with this, but it sounds like you want your relationship to be mapped as a set (to prevent duplicates) rather than a bag. If you map it as a set and use ISet for the property type of the relationship, the duplicates will be handled for you. If however you use a bag, you would need to remove duplicates in your own code. Again, you should be using session.Load to get the group if it's an already existing group.
According to REST philosophy, a PUT operation should (taken from Wikipedia):
PUT http://example.com/resources/142
Update the address member of the collection, or if it doesn't exist, create it.
NHibernate seems to have two ways of dealing with entity IDs:
Auto-generate an ID, regardless of what value the user set.
Use the ID assigned by the user, but lose all auto-generation capabilities.
The problem here with a PUT operation is the part about creating the entity if it doesn't exist. My assumption is that if you PUT a resource that doesn't exist, it will create it with the same ID as specified by the URL (such as 142 if we use the above example). However, NHibernate doesn't allow you to set the ID if it's auto-generated.
So my question is, is there a way to get NHibernate to auto-generate an ID if the entity doesn't have one (or has the default value for the ID type, for example 0 for ints), but also save the entity with the ID that the user set?
Generally its a bad idea to use assigned ids.
The situation that you have is closer to the thing called NaturalId. You should use it I think. You will need to have two different properties, one for databases primary key, and second as a id that is visible to users.