We have the following Domain objects :-
public class UserDevice : BaseObject
{
// different properties to hold data
}
public class DeviceRecipient:BaseObject
{
public virtual UserDevice LastAttemptedDevice{get;set;}
}
Hence the sql schema created based on this using fluent nhibernate automapper is like
DeviceRecipient's table is having primary key of UserDevice as a foreign key i.e UserDevice_Id.
Now, When we try to delete UserDevice object it gives a sql exception for foreign key constraint. What we want to do is :-
Delete the UserDevice object , hence the UserDevice row without deleting the DeviceRecipient as it will be used somewhere else in domain model. We just want to set null to UserDevice_Id column of DeviceRecipient when we delete UserDevice.
We want to do it using fluent nhibernate conventions as we use Automapping.
Any help will be appreciable.. Thanks in advance.!
As I can see you have uni-direction many-to-one relation. So firstly you have to write following override:
public class DeviceRecipientOverride : IAutoMappingOverride<DeviceRecipient>
{
public void Override(AutoMapping<DeviceRecipient> mapping)
{
mapping.References(x => x.LastAttemptedDevice)
.NotFound.Ignore(); // this doing what you want.
}
}
Secondly you could convert it to automapping convention, if you have more places with this behavior.
public class ManyToOneNullableConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
var inspector = (IManyToOneInspector) instance;
// also there you could check the name of the reference like following:
// inspector.Name == LastAttemptedDevice
if (inspector.Nullable)
{
instance.NotFound.Ignore();
}
}
}
EDIT:
From the NHibernate reference
not-found (optional - defaults to exception): Specifies how foreign
keys that reference missing rows will be handled: ignore will treat a
missing row as a null association.
So when you set not-found="ignore" SchemaExport/SchemaUpdate will just not create the FK for you. So if you have the FK then you need to delete it or set OnDelete behavior of the FK to Set Null. Assuming that you are using Microsoft Sql Server:
ALTER TABLE [DeviceRecipient]
ADD CONSTRAINT [FK_DeviceRecipient_LastAttemptedDevice]
FOREIGN KEY ([LastAttemptedDevice_ID])
REFERENCES [UserDevice]
ON DELETE SET NULL
Related
I am having a requirement where if the user enters value for the primary key, then I need to use that when creating an entity and if in case the user does not provide value, the primary key needs to be auto-generated like R00001, R0002 etc.I would like to know how I could achieve this and any guidance on that
Try to take advantage of the IdentifierGenerator interface and define an implementation of your own.
public class MyEntityIdGenerator implements IdentifierGenerator{
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
MyEntity entity = (MyEntity)object;
if(entity.getId()==null){
Connection con = session.connection();
// retrieve next sequence val from database for example
return nextSeqValue;
}
}
}
Then add appropriate annotations on the id field in your entity:
#Id
#GenericGenerator(name="myCustomGen", strategy="com.example.MyEntityGenerator")
#GeneratedValue(generator="myCustomGen")
I am using latest version of Fluent NHibernate automapping. Is there any convention or property I can set to stop creating the foreign key constraints across all the tables? I have nearly 200 classes, So I cannot go to each individual class and property name and set
ForeignKeyConstraintNames("none", "none")
How can we add ForeignKeyConstraintNames("none", "none") in Automapping? I don't want to hardcode the table name or column name. I would like to have the AutoMapping create all the mappings without foreign keys. Basicall don't create any foreign keys across the database. How can we do this?
There is similar POST HERE but the answer was not clear to me.
a simple convention
public class NoForeignKeys : IReferenceConvention, IHasManyConvention
{
public void Apply(IManyToOneInstance instance)
{
instance.ForeignKey("none");
}
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.ForeignKey("none");
}
}
// use it
AutoMap.AssemblyOf().Conventions
.FromAssembly() or .Add(typeof(NoForeignKeys))
I have this problem where I have a one-to-many relationship and I have to be able to delete the parent entity without it's children being deleted, nor their foreign key column set to NULL. But whenever I try deleting a parent, I get the foreign key constraint violation exception.
NHibernate.Exceptions.GenericADOException: could not execute update query[SQL: delete from [Customer]] ---> System.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK7867CB245055157F"
I have a similar one-to-one relationship, where I have to be able to delete the entity on one end without the foreign key on the other end to be set to NULL and I solved this problem successfully by using NotFound.Ignore(). I have found several answers suggesting exactly this solution, but it seems to have no effect at all. I use my mapping to build the database BTW.
Here are my entities and mappings:
public class User : Entity
{
...
public virtual Customer Customer { get; set; }
...
}
public class Customer : Entity
{
...
public virtual string CustomerNumber { get; set; }
public virtual User User { get; set; }
public virtual IList<Vehicle> Vehicles { get; set; }
...
}
public class Vehicle : Entity
{
...
public virtual string SerialNumber { get; set; }
public virtual Customer Customer { get; set; }
...
}
I'm using AutoMapping and overrides:
public class UserMappingOverride : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
...
mapping.References(u => u.Customer).Column("CustomerNumber").NotFound.Ignore();
}
}
public class CustomerMappingOverride : IAutoMappingOverride<Customer>
{
public void Override(AutoMapping<Customer> mapping)
{
mapping.Id(u => u.Kundenummer).GeneratedBy.Assigned().Not.Nullable();
mapping.HasOne(u => u.User).PropertyRef(c => c.Customer);
mapping.HasMany(u => u.Vehicles).KeyColumns.Add("CustomerNumber")
.Cascade.None()
.Inverse();
}
}
public class VehicleMappingOverride : IAutoMappingOverride<Vehicle>
{
public void Override(AutoMapping<Vehicle> mapping)
{
mapping.Id(u => u.SerialNumber).GeneratedBy.Assigned().Not.Nullable();
mapping.References(u => u.Customer).Column("CustomerNumber").NotFound.Ignore();
}
}
As said, in the one-to-one relationship, in the mapping of User I use NotFound.Ignore(), which does as promised - allows me to delete a Customer without firing a constraint violation exception and still keep the values of "CustomerNumber" in the User table intact. The mapping of the relationship between the User and the Customer entities, simply does not produce a foreign key constraint in the database when the database is built from the mapping.
But the same thing doesn't work for my one-to-many relationship. Although the mapping is almost the same as my one-to-one relationship, and I use NotFound.Ignore() as suggested in similar questions here, this relationship still produces a foreign key constraint and I get a constraint violation exception when trying to delete a Customer. The only work-around is to manually delete the FK in the database or modify it by setting Enforce Foreign Key Constraint to False.
How can I get NHibernate to either not create this Foreign key constraint, or setting the Enfore Foreign Key Constraint to False, when building the database?
Best Regards
- Nikolaj
BTW: I'm not interested in comments about the overall design of the entities and relationships. They're designed like this based on constraints from the source of data, and this is the only plausible workaround. :-) I've found that a lot of answers in similar posts, focus on the design rater then of the question at hand.
you can not work around the FK in the database. My guess is that there is no FK from User to Customer. If you create the Schema from mappings then you'll need to disable the creation of the FK with mapping.References(u => u.Customer).ForeignKey("none");
Firo's answer pointed me in the right direction as how to get rid of the FK constraint. Adding .ForeignKey("none") to the Vehicle mapping didn't do it though. But adding a similar property to the Customer mapping solved my problem.
So the solution became:
mapping.HasMany(u => u.Vehicles).ForeignKeyConstraintName("none")
I had a similar question to Fluent NHibernate: How to create one-to-many bidirectional mapping? but I was interested in the situation when I have a one-to-one mapping. For instance
Umbrealla
ID
Owner
UmbreallaOwner
ID
Umbrella
As we know each umbrella can only be owned by one person and nobody owns more than one umbrella. In a fluent map I would have something like
UmbrellaMap()
{
Id(x=>x.ID);
References<UmbrellaOwner>(x=>x.Owner);
}
UmbrellaOwnerMap()
{
Id(x=>x.ID);
References<Umbrella>(x=>x.Umbrella);
}
When creating the tables fluent will create a field in umbrella referncing the ID of umbrellaOwner and a field in umbrellaOwner referencing umbrella. Is there any way to change the mapping such that only one foreign key will be created but the Umbrella property and the Owner property will both exist? The examples I have seen involve setting the relations up in both directions so adding a new Umbrella looks like
AddUmbrealla(UmbrellaOwner owner)
{
var brolly = new Umbrella();
brolly.Owner = owner;
owner.Umbrella = brolly;
session.Save(owner); //assume cascade
}
which seems logical but a bit cumbersome.
Well, a reference is a reference; one object has a reference to the other. The reverse is not necessarily true.
In your case, you MIGHT get away with a HasOne relationship. However, HasOne is normally for denormalized data. Say you wanted more info about the owner, but you could not change Owner's schema because other code depended on it. You'd create an AdditionalOwnerInfo object, and create a table in the schema in which the OwnerID field of the table was a foreign key to Owner, and also the primary key of the table.
Ayende recommends a two-sided References() relationship in 99.9% of one-to-one cases, where the second object is conceptually separate from the first, but there is an implicit "I alone own exactly one thing" type of relationship. You can enforce the "one and one only" nature of the reference using a Unique().Not.Nullable() modifier set on the References mapping.
To streamline the referential setup, consider defining one object (UmbrellaOwner) as the "parent" and the other (Umbrella) as the "child", and in the parent's property setter, set the child's parent to the current reference:
public class Umbrella
{
public virtual string ID { get; set; }
public virtual Owner Owner { get; set; }
}
public class UmbrellaOwner
{
public virtual string ID { get; set; }
private Umbrella umbrella;
public virtual Umbrella Umbrella
{
get{
return umbrella;
}
set{
umbrella = value;
if(umbrella != null) umbrella.Owner = this;
}
}
}
Now, when you assign the child to the parent, the backreference is automagically set up:
var owner = new UmbrellaOwner{Umbrella = new Umbrella()};
Assert.AreEqual(owner, owner.Umbrella.Owner); //true;
I was trying to change a convention so that my IDs follow this simple rule: ProductCode, CustomerCode, OrderCode etc etc.
I've found a simple way to do that adding a convention:
public class PrimaryKeyNameConvention : IIdConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Code");
}
}
Now I've got what I wanted but it seems that FluentNhibernate refuses to apply the same rule with column referencing my primary keys.
EX: my table Customer will have a PK called CustomerCode but my table Order will have a reference column called Customer_Id.
I've tried different ways to rename the column Customer_Id in CustomerCode (table Order) but it seems that nothing works properly.
The only solution which seems to work is adding a convention like this:
public class ReferenceConvention : IReferenceConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IManyToOneInstance instance)
{
instance.Column(instance.Property.PropertyType.Name + "Code");
}
}
but now FluentNhibernate creates two columns which reference my primary key: CostumerCode and Customer_Id.
I can't figure out what I am doing wrong.
Any help would be apreciated.
Regards,
Alberto
Take a look at the ForeignKeyConvention base-class.
The ForeignKeyConvention is an amalgamation of several other conventions to provide an easy way to specify the naming scheme for all foreign-keys in your domain. This is particularly useful because not all the foreign-keys are accessible in the same way, depending on where they are; this convention negates need to know about the underlying structure.
As James suggested I've now applied these two conventions:
public class PrimaryKeyNameConvention : IIdConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Code");
}
}
public class CustomForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if (property == null)
return (type.Name + "Code"); // many-to-many, one-to-many, join
return (property.Name + "Code"); // many-to-one
}
}
and everything works fine.