Retrieving records with null value - fluent nhibernate - c#

I have a situation where one of my table is self-mapped to itself. The primary key of one row (Parent) can be used as a foreign key to other row (Child) and this foreign key column contains null for such rows that have no parent. Something like this:
table: Settings_LocationType
++++++++++++++++++++++++++++++++++++++++++++++++
LocationID | Name | ParentLocationId
++++++++++++++++++++++++++++++++++++++++++++++++
1 Parent 1 null
2 Child 1 1
3 Child 2 1
4 Parent 2 null
Model: LocationType
public class LocationType
{
public virtual long LocationTypeId { get; set; }
public virtual string Name { get; set; }
public virtual LocationType ParentLocationType { get; set; }
public virtual IList<LocationType> LocationTypes { get; set; }
public LocationType()
{
LocationTypes = new List<LocationType>();
}
}
Mapping: LocationTypeMap
public class LocationTypeMap : ClassMap<LocationType>
{
public LocationTypeMap()
{
Table("Setting_LocationType");
Id(x => x.LocationTypeId).Column("LocationId").GeneratedBy.Sequence("location_type_seq");
Map(x => x.ShortName, "Name").Length(15).Not.Nullable();
References<LocationType>(x => x.ParentLocationType).LazyLoad().Nullable();
HasMany<LocationType>(x => x.LocationTypes).AsBag().KeyColumn("ParentLocationId").KeyNullable().LazyLoad().Inverse().Cascade.SaveUpdate();
}
}
Now I am having a problem in retrieving those rows which contain NULL (or say aren't child) in PatentLocationType field. I tried passing null like this repo.Get("ParentLocationType.LocationTypeId", null); but it didnt work but threw object reference is not set to an instance error.

Have you tried:
repo.Get ("ParentLocationType", null)

OK, I solved it using Expression.IsNull instead of Expression.Eq when querying for such LocationType

Related

How to remove category without sub category

How to remove category without sub category ?
Category Model :
public class Category
{
public virtual int Id{ get; set; }
public virtual string Name { get; set; }
public virtual Category Parent { get; set; }
public virtual int? ParentId { get; set; }
}
datas :
Id ParentId Name
1 null Hot
2 1 Soup
3 1 Coffee
4 3 Decaf Coffee
5 null Cold
6 5 Iced Tea
i need to remove Category with Id=1 but The following error occurs :
The DELETE statement conflicted with the SAME TABLE REFERENCE
constraint "FK_dbo.Categories_dbo.Categories_ParentId". The conflict
occurred in database "ProjectDatabase", table "dbo.Categories", column
'ParentId'.
The statement has been terminated.
my delete code :
public void Delete(int categoryId)
{
var category = _categories.First(d => d.Id == categoryId);
_categories.Remove(category);
}
CategoryConfig :
public class CategoryConfig : EntityTypeConfiguration<Category>
{
public CategoryConfig()
{
ToTable("Categories");
HasOptional(x => x.Parent)
.WithMany()
.HasForeignKey(x => x.ParentId)
.WillCascadeOnDelete(false);
}
}
Well, according to the documentation, if a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship (which you are doing explicitly), and when the principal is deleted the foreign key will be set to null.
I don't know why is not doing that in your case, probably is because you are working with an unidirectional relationship and you don't have the collection of children in your parent Category, so EF couldn't set the FK properties to null in the subcategories, but you can try the following:
var category = _categories.First(d => d.Id == categoryId);
var children=_categories.Where(d=>d.ParentId==categoryId);
foreach(var c in children)
c.ParentId=null;
_categories.Remove(category);
You can't delete the category with Id=1 because there are subcategories referenced to it (Soup and Coffee). In your delete method you'll have to either change the ParentId of these items first to another existing element (or null) or delete these items before deleting the category with Id=1.

Nhibernate stores id=0 as null

I have small problem with nHibernate (fluent) I have two objects, one contains another - a parent and a child (predefined objects, readonly).
mappings:
public class ParentClass
{
public virtual int Id { get; set; }
public virtual ChildClass Metoda { get; set; }
}
public ParentClassMap() {
Table("Wyceny");
Id(x => x.Id).Column("Id").GeneratedBy.TriggerIdentity();
References(x => x.Metoda).Column("RMW_ID");
}
public ChildClass
{
public virtual int Id { get; set; }
public virtual string Nazwa { get; set; }
}
public ChildClassMap()
{
Table("Metody");
Id(x => x.Id).Column("Id");
Map(x => x.Nazwa).Column("Nazwa_met");
}
Everything works perfectly until I chose child object with id = 0, reading still works for id=0, but when I'm trying to save or update Parent with correct ChildObject(readed previously from db through nHibernate), nHibernate stores null instead of value.
Any suggestions?
nHibernate 3.3.1.4000
fluent 1.4.0.0
The issue here would be the unsaved-value. NHibernate must decide if operations with object will be insert or update. This decision comes from unsaved-value setting, which is by default for int set to 0.
Try to extend your mapping of a ChildClass:
public ChildClassMap()
{
Table("Metody");
Id(x => x.Id)
.Column("Id")
.UnsavedValue(-1);
...
See 5.1.4. id, cite:
unsaved-value (optional - defaults to a "sensible" value): An identifier property value that indicates that an instance is newly instantiated (unsaved), distinguishing it from transient instances that were saved or loaded in a previous session.
And here is nice Id mapping overview by Adam Bar (the second half of the article)

EF6: Code First Complex Type

I'm having trouble getting entity framework to flatten my domain entity classes with Value Objects (complex type) fields to one table.
Everything works if I tell my model builder to ignore my value objects/complex type, but that results in all the attributes of the value object being missed in my tables. As soon as I remove the ignore statement i get "A value shared across entities is created in more than one location". If I look in the resulting CE SQL file I see an additional table named after my Domain class appended with a 1 and containing only the Value Object parameters.
Some Code:
My domain Classes:
public User {
private User(){}
public long Id {get; private set;} // dont ask, inherited legacy database
public string UserId { get; private set; }
public string Domain { get; private set; }
public AuditIformation AuditDetails {get ; private set;}
//..domain logic etc
}
public AuditInformation : IValueObject {
public long CreatedByUserId { get; private set; }
public DateTime CreatedDate { get; private set; }
}
My repository project (going code first) has got this:
public partial class myContext : DbContext {
protected override void OnModelCreating(DbModelBuilder mb) {
mb.Conventions.Remove<PluralizingTableNameConvention>();
mb.ComplexType<Domain.Model.AuditInformation>();
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedDate).HasColumnName("Created_On");
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedByUserId).HasColumnName("Created_By");
//This line lets everything work but doesn't include my
//AuditInformation attributes in my User Table.
mb.Ignore<Domain.Model.AuditInformation>(); // <== I think I need to remove this
//..
mb.Entity<User>().Map(a => {
a.Property(x => x.Id).HasColumnName("Id");
a.Property(x => x.UserId).HasColumnName("User_Id");
a.Property(x => x.Domain).HasColumnName("User_Dmain");
})
.HasKey(x => x.Id)
.ToTable("Tbl_User"); //<==Again, dont ask
}
}
What I want to get is a table looking like:
[TBL_USER]
ID AS BIGINT,
USER_ID as VARCHAR(MAX),
USER_DMAIN AS VARCHAR(MAX),
CREATED_ON as DATE,
CREATED_BY as BIGINT
But what im getting is only:
[TBL_USER]
ID AS BIGINT,
USER_ID as VARCHAR(MAX),
USER_DMAIN AS VARCHAR(MAX),
and if I remove the ignore line i get this bonus freak table
[USER1] <<==Note, named after the domain class, not the destination table..
ID AS BIGINT,
CREATED_ON as DATE,
CREATED_BY as BIGINT
and a whole bunch of error when I try to use my repository:
----> System.Data.Entity.Infrastructure.DbUpdateException : A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
----> System.Data.Entity.Core.UpdateException : A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
----> System.ArgumentException : An item with the same key has already been added.
TearDown : System.NullReferenceException : Object reference not set to an instance of an object.
Ive done a lot of searching but I just cant find any concrete examples of persisting my value object attributes into the tables created for my domain objects. Can someone show me where I'm going wrong?
Try this:
public class AuditInformation
{
public long CreatedByUserId { get; set; }
public DateTime CreatedDate { get; set; }
}
public abstract class AuditInfo
{
public AuditInformation AuditDetails { get; set; }
public AuditInfo()
{
this.AuditDetails = new AuditInformation();
this.AuditDetails.CreatedByUserId = 0;
this.AuditDetails.CreatedDate = DateTime.Now;
}
}
public User : AuditInfo
{
private User(){}
public long Id {get; private set;} // dont ask, inherited legacy database
public string UserId { get; private set; }
public string Domain { get; private set; }
//..domain logic etc
}
public partial class myContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Conventions.Remove<PluralizingTableNameConvention>();
mb.ComplexType<Domain.Model.AuditInformation>();
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedDate).HasColumnName("Created_On");
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedByUserId).HasColumnName("Created_By");
mb.Entity<Cricketer>().Map(a =>
{
a.Property(x => x.Id).HasColumnName("Id");
a.Property(x => x.UserId).HasColumnName("User_Id");
a.Property(x => x.Domain).HasColumnName("User_Dmain");
a.Property(x => x.AuditDetails.CreatedByUserId).HasColumnName("CreatedByUserId");
a.Property(x => x.AuditDetails.CreatedDate).HasColumnName("CreatedDate");
})
.HasKey(x => x.ID)
.ToTable("Tbl_User"); //<==Again, dont ask
}
}

Allow duplicates in Many-to-Many?

I have Item table which have manyTomany to itself:
public virtual ICollection<Item> Related { get; set; }
modelBuilder.Entity<Item>().HasMany(x => x.Related ).WithMany();
Data:
Item_Id Item_Id1
8 2
8 3
8 4
How can I allow duplicate , meaning allow another 8-2 for example, when I try to insert another "duplicate" data , it shows only 1.
I think that the origin of the problem is to misunderstand what an entity is. An entity is unique, and has a unique Id, so for example, a company could have a collection of employed people, that are unique, so you cannot have two instances of the same person in the same collection. If you have a shopping cart, you have to think to the items in the collection as 'order lines' and not as the items itself. Any order line has its own Id and a reference to an entity, that is the object you are purchasing.
I didn't check this specifically, so you may need to work out the details -
but (I think) the only way to do it is to manually define the index table (e.g. Item2Item).
Check this post I made for additional info
EF code-first many-to-many with additional data
That is on how to create a custom table for many-to-many - with adding
additional fields.
Now that you have a custom table, you need to change it a bit - you need to remove the composite keys from the .HasKey. Instead use your own pk, e.g. identity as for any other table.
i.e. you can define a unique PK, identity like for any other table - and have that as your key. That should let you have duplicate keys in your fk columns).
Check this post as it seems logically close (though not the same)
Many to many (join table) relationship with the same entity with codefirst or fluent API?
Well this gave me the final solution--> EF Code First, how can I achieve two foreign keys from one table to other table? , NSGaga thanks for the direction
public class Item : IValidatableObject
{
[Key]
public int ID { get; set; }
public virtual ICollection<ItemRelation> Related{ get; set; }
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (....)
{
yield return new ValidationResult("Name not valid", new[] { "Name" });
}
}
}
public class ItemRelation
{
[Key]
public int ID { get; set; }
public int ItemAID { get; set; }
public virtual Item ItemA { get; set; }
public int ItemBID { get; set; }
public virtual Item ItemB { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<ItemRelation>().HasRequired(c => c.ItemA)
.WithMany(m => m.Related)
.HasForeignKey(c => c.ItemAID)
.WillCascadeOnDelete();
modelBuilder.Entity<ItemRelation>().HasRequired(c => c.ItemB)
.WithMany()
.HasForeignKey(c => c.ItemBID)
.WillCascadeOnDelete(false);
}
public DbSet<Item> Items { get; set; }
public DbSet<ItemRelation> ItemsRelations { get; set; }
Data:
Id ItemA_Id ItemB_Id
1 8 2
2 8 3
3 8 5
4 8 5
5 8 5
6 1 6
7 5 4
8 2 9

Mapping a single field to multiple tables with Fluent NHibernate

Question: is there a way to map a single foreign key to a number of mutually exclusive tables, based on a context?
Background...
In my specific example, I have the following domain graph, representing an insurance claim which can be against a vehicle or property:
public enum InvolvedPartyContext
{
Vehicle = 1,
Property = 2
}
public class Claim
{
public virtual Guid Id { get; set; }
public virtual InvolvedPartyContext InvolvedPartyContext { get; set; }
public virtual Vehicle Vehicle { get; set; } // set if Context = Vehicle
public virtual Property Property { get; set; } // set if Context = Property
}
public class Vehicle { //... }
public class Property { //... }
The SQL looks like this (notice the single foreign key InvolvedPartyId):
CREATE TABLE Claims (
Id uniqueidentifier NOT NULL,
InvolvedPartyContext int NOT NULL,
InvolvedPartyId uniqueidentifier NOT NULL
)
CREATE TABLE Vehicles (
Id uniqueidentifier NOT NULL,
Registration varchar(20) NOT NULL
)
CREATE TABLE Properties (
Id uniqueidentifier NOT NULL,
PostCode varchar(20) NOT NULL
)
The Fluent NHibernate mapping file for Claim:
public ClaimMap()
{
Id(x => x.Id);
Map(x => x.InvolvedPartyContext).CustomTypeIs(typeof(InvolvedPartyContext));
References(x => x.Vehicle, "InvolvedPartyId");
References(x => x.Property, "InvolvedPartyId");
}
This throws an "Invalid index {n} for this SqlParameterCollection with Count {m}" exception, since the same field (InvolvedPartyId) is mapped twice. A simple fix would be to create VehicleId and PropertyId fields, but in the real world there are many more contexts, so this isn't very flexible.
Personally, I wouldn't go with the design you have. Instead I'd create subclasses of your Claim class, VehicleClaim and PropertyClaim respectively.
public class VehicleClaim : Claim
{
public virtual Vehicle Vehicle { get; set; }
}
Then change your mappings to use your InvolvedPartyContext column as a discriminator (the column which NHibernate uses to determine which class the row represents), and create subclass mappings for each subclass.
public class ClaimMap : ClassMap<Claim>
{
public ClaimMap()
{
Id(x => x.Id);
DiscriminateSubClassesOnColumn("InvolvedPartyContext");
}
}
public class VehicleClaimMap : SubclassMap<VehicleClaim>
{
public VehicleClaimMap()
{
DiscriminatorValue(1);
References(x => x.Vehicle);
}
}
If you really do want to run with what you've got, you should look into the any mappings; there isn't a lot of documentation on them, but you use the ReferencesAny method.

Categories