Using Entity Framework with Breeze, I have this class Taxi with nullable int TravelID:
public class Taxi
{
// some primary key/Id stuff
// Then these:
public int? TravelID { get; set; }
public virtual Travel Travel { get; set; }
}
(simplified of course) Mapping looks like this:
public TaxiMap()
{
this.Property(t => t.TravelID).IsOptional();
}
My database tool (HeidiSQL) shows that the property is nullable and the default is also NULL. But when I try to save a Taxi entity I get this validation error:
errorMessage: "'TravelID' is required"
The TravelID was required before but I changed that like this. This error occurs when the TravelID is null. When it's 0 I get a FK constraint error.
So now my question is, did I forget anything/do something wrong to make the nullable work?
Added after 1 hour: I have found a workaround for now but it's really dirty and i'd really prefer not using it. In the front end I set the TravelID to '0' to pass the validation, then in my controller I set the Taxi's TravelID to NULL before saving.
The TravelID is not the actual ID of your Taxi class, it is a foreign key to the Travel class. The mapping you're trying to make work here should be in the TravelMapper, as it belongs there.
If you take it to the next step, you can omit all of this and use attributes on your models, such as the autogeneration attribute above the ID property:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
This way you ensure the Travel class must have an ID but in the Taxi class there is nothing that obligates you to specify an object of type Travel.
Related
I am trying to use Nullable Guid in Entity Framework but when i tried to commit the change i am getting validation message due to foreign key is null, I have below domain class, DomainType Class has child DomainCustomField :-
DomainType.cs
public class DomainType
{
public Guid? Id
{
get;
set;
}
private IList<DomainCustomField> _domainCustomFields;
public string Name
{
get;
set;
}
public Guid? AccountId
{
get;
set;
}
public virtual IList<DomainCustomField> DomainCustomFields
{
get
{
return this._domainCustomFields;
}
set
{
this._domainCustomFields = value;
}
}
}
DomainCustomField.cs
public class DomainCustomField
{
public Guid? Id
{
get;
set;
}
public string Name
{
get;
set;
}
public Guid? DomainTypeId
{
get;
set;
}
}
below is mapping :-
modelBuilder.Domain<DomainCustomField>().HasKey((DomainCustomField p) => new
{
p.Id,
p.DomainTypeId
});
modelBuilder.Domain<DomainCustomField>()
.Property(p => p.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Domain<DomainType>()
.HasMany(p => p.DomainCustomFields)
.WithRequired()
.HasForeignKey<Guid?>(x => x.DomainTypeId);
below is sample code which i tried to insert.
DomainType domainType = new DomainType()
{
AccountId = customer.Id
};
domainType.DomainCustomFields.Add(new DomainCustomField()
{
Name = "Test"
});
Workspace.Add<DomainType>(domainType);
Workspace.CommitChanges();
Error :- validation error for DomainTypeId is required
in the same code if i change Guid? to Guid in ID variable then its work correctly. I don't know how to handle nullable Guid. do i need to manually set parent key in child reference? then how it works fine in case of Guid?
Note :- I am using using Guid because i am working with distributed database, where id can generate from multiple source system.
I am using Guid? because by default Guid generate 00000000-0000-0000-0000-000000000000 for empty Guid which can grow database size.
The issue has nothing to do with Guid type (the same will happen with int), but the discrepancy between the nullability of the property type used in the entity and the actual nullability of the property implied by the model.
First, EF requires PKs to be non nullable. Hence DomainType.Id, DomainCustomField.Id and DomainCustomField.DomainTypeId are effectively non nulable (it's unclear why you have include DomainTypeId in the PK of the DomainCustomField which already have unique database generated Id, but that's another story).
Second, DomainCustomField.DomainTypeId is effectively non nullable because you have specified that - the WithRequired() part of the FK relationship configuration.
Now, EF does its best to handle these discrepancies. If you look at the generated migration, you will see that the corresponding database table columns of all the fields in question are non nullable. However (nothing is perfect, you know) some part of the EF code is failing to handle that discrepancy and hence the exception.
With that being said, always keep the model properties nullability in sync with their actual nullability. Which in you case is to make both 3 properties non nullable (i.e. of type Guid).
P.S. What about the note I am using Guid? because by default Guid generate 00000000-0000-0000-0000-000000000000 for empty Guid which can grow database size., I have no idea what do you mean by that. The nullablitity of the class property has nothing to do with the database size, and as I already mentioned, the corresponding database table columns for such setup are non nullable anyway.
Also note that in your example you forget to set the Id of the new DomainType, which you should because it's not auto generated.
I'll take a slightly different approach in my answer and try and tell you what to change in order to get a working example;
Like someone has pointed out, unique identifiers must not be nullable so the Id field in DomainType should a Guid. Also you will need to change the Id field in DomainCustomField. These may need an empty constructor just to create a new Guid.
Secondly, the foreign key in your other object Guid? DomainTypeId is perfectly fine, this can stay, but if it is staying you will need to change your configuration.
In this block you have specified that the property is required?
modelBuilder.Domain<DomainType>()
.HasMany(p => p.DomainCustomFields)
.WithRequired()
.HasForeignKey<Guid?>(x => x.DomainTypeId);
So simply make it optional;
modelBuilder.Domain<DomainType>()
.HasMany(p => p.DomainCustomFields)
.WithOptional()
.HasForeignKey<Guid?>(x => x.DomainTypeId);
That should solve your issues. Any questions let me know :)
You can use a standard identifier for the database. GUID? stored as a field in the table.
Background
I have a class that looks more or less like this:
public class MyClass
{
[Id]
public long Id { get; set; }
public string MyProperty { get; set; }
public bool MyBoolean { get; set; }
public string AnotherProperty { get; set; }
public MyClass ChildOne { get; set; }
public MyClass ChildTwo { get; set; }
}
I will need to use a stored procedure to load a set of records, but that's ok as long as the structure itself is correct.
For any instance of MyClass, one or both of the children can be null. Any instance of MyClass can be used in a parent class - but the child itself doesn't need to know about this relationship, and a child can be used by any number of parents.
Problem
With this structure, I get the following error when creating a new migration:
Unable to determine the principal end of an association between the
types 'MyClass' and 'MyClass'. The principal end of this association
must be explicitly configured using either the relationship fluent API
or data annotations.
This error makes sense - when given a structure of an object with a foreign key to itself, I am not surprised that EF has a hard time determining the principal end. I'm not sure how to fix this, though.
I've tried some different Fluent mappings:
modelBuilder.Entity<MyClass>().HasOptional(x => x.ChildOne).WithOptionalPrincipal(x => x.ChildOne);
modelBuilder.Entity<MyClass>().HasOptional(x => x.ChildOne).WithOptionalDependent(x => x.ChildOne);
modelBuilder.Entity<MyClass>().HasOptional(x => x.ChildOne);
(Note: I didn't try these concurrently - I did one at a time & duplicated it for ChildTwo.)
I was able to get a migration to work by adding a ChildThree property to MyClass, but that doesn't make sense and isn't a useful property; it just creates another foreign key on the table but this isn't needed in my model.
So, in summary:
How do I get this structure to work the way I want? I think the secret is in some Fluent mapping voodoo but I'm very unfamiliar with that library and I don't know how to get that to work.
Why does adding a third (unneeded, unwanted) property fix everything and allow the migration to scaffold?
Your fluent mapping is totally wrong.
You should do something like this:
modelBuilder.Entity<MyClass>().HasOptional(p => p.ChildOne).WithOptionalDependent();
modelBuilder.Entity<MyClass>().HasOptional(p => p.ChildTwo).WithOptionalDependent();
I'm setting up a model for a project and everything works as expected, except for the change below. I thought it'd be neat to specify the type as Type instead of its description as string.
namespace DataBase.Entities
{
public class Lock
{
public Guid Id { get; set; }
public DateTime? Occasion { get; set; }
public int Counter { get; set; }
public Type Entity { get; set; }
//public string Entity { get; set; }
}
}
Guess what! EF didn't like it one bit. The error I get when adding an explicit migration then is as follows and I have no idea why.
System.ArgumentNullException: Value cannot be null.
Parameter name: entitySet
Most of the goolearching resulted in people discovering that the POCO classes had some inheritance they forgot about. Someone suggested to force re-enabled migrations. I don't have any inheritance at all in the model and forcing the migrations gave nothing but a recreated configuration file.
I would always store the assembly-qualified type name instead of the type itself.
A Type instance it's not just a name, but a lot of metadata that may be interesting during run-time, but it would be pointless to store (i.e. serialize) a type instance as is.
When you set the whole Type property get Type.AssemblyQualifiedName property value:
instance.Type = typeof(X).AssemblyQualifiedName;
And the code that should work with the whole type could call Type.GetType(lock.Type) to build a Type instance again.
I am trying to map a vertical inheritance between a base class and derived class (obviously). I am using code-only and the FluentAPI approach for which I have found almost ZERO documentation. I have found a couple of docs on vertical inheritance and code-only but very few on managing the discriminator column/value.
So I have been trying to extrapolate how to do it from a combination of this blog post and some documentation on implementing vertical inheritance using code-only. All to no avail.
You will see that I have a "Deliverables" base table and "PrintDeliverables" derives from that. There will be other derivatives coming down the road. But I figured I would start with one first.
Anyway, I naturally have models that map to the tables.
public class PrintDeliverable : BDeliverableBase
{
public String PaperItemNumber { get; set; }
public String PrinterModel { get; set; }
public Boolean? ColorOption { get; set; }
public String ProductCode { get; set; }
}
public class BDeliverableBase : BModelBase, IDeliverable, ISingleID
{
public Int64 ID { get; set; }
public String Label { get; set; }
public String Description { get; set; }
public IList<DeliverableAttribute> Attributes { get; set; }
public Int64 TypeID { get; set; }
public DeliverableType Type { get; set; }
}
public class DeliverableType : BModelBase, ISingleID
{
public Int64 ID { get; set; }
public String Label { get; set; }
public String Description { get; set; }
public IList<BDeliverableBase> Deliverables { get; set; }
}
I have standard mapping which maps the fields and types, sizes, etc. When I run it with no further additions I get the Error ...
Invalid Column name voa_class
My research uncovered that the ORM is attempting to update a "discriminator" column with a value that will tie the base table and table with the derived data together. I learned that I can change the name of the column it uses, which I did in the BASE CLASS mapping (BDeliverableBase). I changed it to use the "DeliverableTypeId" column since the DeliverableType indicates which TYPE of deliverable it is. Since each TYPE will have it's own derivative table this would be an appropriate value to associate which derivative table to use.
MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
map.HasDiscriminator().ToColumn("DeliverableTypeId");
It appears to like this better but it wants to insert this crazy number (ex// 612274703-854) into the DeliverableTypeId column which, of course, being a foreign key to the DeliverableTypes table is not allowed.
Insert of '612274703-' failed: Telerik.OpenAccess.RT.sql.SQLException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_DeliverableType". The conflict occurred in database "DB1", table "dbo.DeliverableTypes", column 'DeliverableTypeId'
I learned that OpenAccess/DataAccess generates a hash value to insert into the discriminator column. I do not want this, in fact I know that the value must be one of the IDs available in the DeliverableType. So I read in one of the docs that you could define what value to assign to the discriminator. The example applied a hard-coded value to the base class (dog and cat derived from animal) ...
animal.HasDiscriminatorValue("23");
This presented one problem ... I do not have a single value I can hard-code. It could one of MANY values present in the DeliverableTypes table. However, for the sake of proving out the concept I hard-coded the value of an existing record
MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
map.HasDiscriminator().ToColumn("DeliverableTypeId");
map.HasDiscriminatorValue("819");
I continued to get the identical error from before. So it doesn't appear that it was applying my hard-coded value. So ... I thought, while hard-coding the value is a little hacky it would make more sense to define that in the mapping for the derived class. That would resolve my hard-coded issue since ALL instances of that derived class WOULD indeed be of the same DeliverableTypeId. So I tried ...
MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
map.HasDiscriminator().ToColumn("DeliverableTypeId");
MappingConfiguration<PrintDeliverable> map = new MappingConfiguration<PrintDeliverable>();
map.HasDiscriminatorValue("819");
This resulted in the Error
Insert of '612274703-857' failed: Telerik.OpenAccess.RT.sql.SQLException: String or binary data would be truncated.
So I got a different error but still the same poblem. This type it was trying to stuff the ORM generated discriminator value (instead of my 819) into what I am assuming is my defined discriminator column (DeliverableTypeId), although the different error makes me suspicious that it was targeting a different column.(?)
In an effort to not drag this out too long I have tried several combinations of where to these "HasDiscriminator" and "HasDiscriminatorValue" assignments go but always end up with one or the other of these errors. So the question is ...
How, using code-only, do I map Vertical Inheritance using multiple, existing "type" values?
I am running into some errors implementing my plan as described below. I am not so interested at this point in resolving particular errors as I am in whether or not this is a good idea.
All history-capable objects descend from a common class AuditableObject with a single property public Guid ID { get; set; }.
A descendent might be:
public class Taco : AuditableObject { public string Seasoning { get; set; } }
Now, I would like to implement the save event handler to write to the following table (class)
public class AuditItem
{
public Guid ID { get; set; }
public virtual AuditableObject Object { get; set; }
public string ObjectClassName { get; set; } //ugly
public string OldObjectXMLData { get; set; }
public string NewObjectXMLData { get; set; }
public DateTime Timestamp { get; set; }
}
I am not sure if I need the ObjectClassName as I can check the type of the object at runtime but it's there just in case.
On save I would basically serialize the object before and after to the respective properties, save a timestamp, and set the object - ie map the FK.
Is this an ugly way to go about it? Are there any obvious drawbacks to descending from a single class with EF Code First as I am doing?
I think you will need the full-qualified-name of the object's type when desalinizing, so that will be mandatory.
Alternatively serializing the object will be cause you problems. Assume that we are going to audit TacoOject1 of Taco class using the approch, the serialized data will be put in data base, later due to business changes we need to add another property to Taco, after recompilation when we need to deserilazed TacoOject1 we will get TypeMissMatchException (not sure of exception name).
Another design objection is using inheritance for audit process.
First: In reality Taco is not a AuditableObject , Its a roll played by Taco, using inheritance will violate Liskov Substitution Principle.
Second: You can not use multiple inheritance, think that if we had a TacoSupperClass, how we could audit Taco then?
If I where going to design auditing process, I would use Entity–attribute–value model Making AuditItem a marker interface and rename it to IAuditableEntity.
Having an attribute called AuditableProperty would enhance our process.
Any entity needs to be audited will be marked by IAuditableEntity, any property of the entity needed to be partcipated in audit will be marked by AuditableProperty attribute.
public class Taco : IAuditableEntity
{
[AuditableProperty]
public string Seasoning { get; set; }
[AuditableProperty]
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
}
The AuditLog table will have these columns:
1. EntityFullTypeName: (String) We are going to audit different entities, the field will be used to get meaningful reports .(mandatory)
2. ObjectIdentifier: Entity identifier that is being manipulated, primary key or business key of the entity.
3. FieldName: (String) Entity field name.
4. OldValue: (String) Entity field old value.
5. NewValue: (String) Entity field new value.
6. TransactionUser: Application user that makes the change. (mandatory)
7. TransactionID: Any operation changing the entities will need to have a unique transaction ID (like GUID) (mandatory), In case of an update on an entity changing multiple fields,these column will be the key point to trace all changes in the update(transcation)
8. ChangeDate: Transaction date. (mandatory)
9. FieldType: enumeration or text showing the field type like TEXT or Double. (mandatory)
In service layer when Taco1 is going to be updated(or inserted) we will check if Taco1 type is marked by IAuditableEntity using reflection(using a lazy chash to store reflection data), if so which properties have been changed(we need a separate DB call to fetch old values).
e.g :
Taco1 = new Taco();
Taco1.Seasoning = "old Seasoning value";
Taco1.OtherProperty1 = "Old Other Property1 value";
Taco1.OtherProperty2 = "Old Other Property2 value";
Saved before,now updating:
Taco1.Seasoning = "New Seasoning value";
Taco1.OtherProperty1 = "New Other Property1 value";
Taco1.OtherProperty2 = "New Other Property2 value";
We will insert two records in AuditLog with the same TransactionID:
Having this approach Any entity (table) could be traced Reports will be readableOnly changes will be logged.