Update query with NHibernate - c#

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.

Related

Entity Framework Navigation Property with Composite Foreign Key

EF 6.1.3.
I have a domain which contains many instances of a "Header/ Item" type pattern, where the a Header can have many Items (1 to many), and also has a "current" or "latest" item.
This is represented as follows:
Header
Guid Id
Guid CurrentItemId
Item CurrentItem
ICollection<Item> AllItems
Item
HeaderId
Id
The PK of the Items is always the HeaderID + ItemID. The reason being that, by far, the most common access pattern for items is to list all items related to a given header, and having HeaderID be the first part of the PK/clustered index means we get that data with clustered index seeks.
Our problem is that when we use the CurrentItem navigation property, it only ever uses the ItemID to do the lookup, which results in not so great query plans.
I assume this is because the conventions for EF us to use the CurrentItemId to look up the CurrentItem. My question is, is there a way for my to tell EF to always perform its joins for CurrentItem by mapping the Header.Id,Header.CurrentItemId -> Item.HeaderId,Item.Id?
I believe this is a slight different scenario than the one described here: composite key as foreign key
In my case, I have a one to one mapping not one top many, and there doesn't seem to be a WithforeignKey method available for that scenario.
We ended up not being able to get EF to generate the SQL the way we wanted - so we wrote a db command interceptor to dynamically find instances of this join and re-write the join to match our designed composite key.
We configure this as the DbContext level like so:
this.ModifyJoin<Item, Header>(
(i) => new Header() { CurrentItemId = i.Id }, //What to find
(i) => new Header() { CurerntItemId = i.Id, Id = i.HeaderId }); //What to replace with
This information is attached to the context instance itself, so when the command interceptor sees the overrides, it uses them to re-write the SQL.
This ends up working well for most scenarios, but there are some - such as when additional filtering is doing on the Item table as part of the LINQ statement, that the aliasing rules used by EF become too complex to follow without writing a full SQL parser.
For our use, this results in the ideal join about 90% of the time, which is good enough for us.
The code to do all this isn't difficult, but it's too big to put here. Add a comment if you want a copy and I'll put it up on GitHub.

EF6 Code First : Entity with multiple relationships

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

Find entities dependent on other entities (via DB ForeignKeys) in DataContext

I am writing some very generic database code, and I would like to find a way to query a set of types in my entity set (using LINQ to SQL EntityFramework, database-first) which are related to another entity.
So, for example I might have a Product belong to a Category;
SELECT * FROM Products p INNER JOIN Categories c ON p.CategoryId = c.Id
In my database, p.CategoryId is a Foreign Key constrain to be NON-NULL. (i.e; a Product MUST belong to a Category).
Now, if I try to DELETE FROM Categories, I get errors, as there are Products still related to Categories, and I do not have (or want) a CASCADE DELETE applied to the relationship.
What I want to do is examine my DataContext library, and determine dynamically that Categories are key'd by Products, and so if I want to DELETE from Categories, I will first need to DELETE from Products.
By 'dynamically', I mean that my code will not have fore-knowledge of the types or relationships (it does not know about Product or Category entities specifically) but I am willing to use Reflection to assess the DLL to work out these relationships.
I have looked at the DataContext-related code, and can see EntityRef properties on certain Entities (which is half the problem solved), but I can't seem to find any way to tell if these relationships are mandatory or optional (i.e; NON-NULL or NULLABLE) in the underlying database.
?
You know you can automatically cascade deletes in Entity Framework by setting the Delete Rule to "Cascade"? That may make things easier (while not necessarily providing a generic solution for LINQ to SQL).
Take a look here for more info:
http://msdn.microsoft.com/en-us/library/bb738695.aspx

navigation property to soft-deleted entity

I have 2 entites:
Im my DB they look like:
Vehicles(Id, VehicleNumber, IsDeleted, WorkerId)
Workers(Id, Name, Address)
And in my edmx:
VehicleId: Id, VehicleNumber, IsDeleted, WorkerId, Worker
Workers: Id, Name, Address, VehiclesList
As you can see, Vehicles table contains soft deleted rows. Now when I get Worker with id=2, I got all his vehicles, including the one I soft deleted. How can I retrieve only the undeleted vehicles?
Badly. EF has very limited support for soft deletes. Actually the only possibility is using conditional mapping where you explicitly hardcode (it cannot be changed at runtime) to your mapping condition saying that you don't want to load entities having IsDeleted = 0. Check mapping details:
But it has very bad consequences:
IsDeleted column cannot be mapped - it already defines mapping internally
Your model can never be used to load soft deleted entities even if you want
The first problem can be solved by mapping stored procedure to delete operation for the Vehicle entity and the second problem can be solved by separate model for auditing and retrieving deleted entities.
Also conditional mapping is not supported by code first - it requires EDMX file.
is lazyloading enabled? then try to limit result set with a where:
worker.VehiclesList.Where(x=>!x.IsDeleted)
also you can put a condition to a vehicles table mapping in model desiner isdeleted = false. Soft deleted vehicles will not be retrived at all

Define an association with a filter?

Is there a way to create an association in entity framework that always applies a "where" filter?
Here's an example:
Suppose that I have a database that uses "soft" deletes -- so there is a "deleted" column in every table.
So if I have a Customer entity that is related to Address (1:0..n), I would like for the Addresses navigation property to always be filtered on the deleted flag, so that if I go Customer.Addresses, the collection will only return Addresses where deleted == 0.
Anyone know of some way to do that?
No, there isn't.
Same reason you can't have a conditional foreign key.
A workaround could be to use TPH on the Address table:
Create abstract entity called Address
Create derived entities called AddressDeleted and AddressNotDeleted
Set the discriminator mapping (AddressDeleted maps to Address when Deleted = 0)
Create a navigational property between Customer and AddressDeleted.
So when you do ctx.Customer.Include("AddressDeleted").Single() it will only return the addresses that are flagged as deleted.
Or you could defined an association to Address, and use ctx.Customer.Addresses.OfType<AddressDeleted>().
Does that solve your problem?

Categories

Resources