EF CTP4: How to tell EF a column is NOT identity - entity-framework-4

I have a code-first, POCO project in which I am trying to adjust an existing database so that it syncs up with what EF is expecting, given my existing model.
I have these entities:
public class FlaggedDate
{
[Key]
public long scheduledDayID { get; set; }
[Required]
public DateTime date { get; set; }
[StringLength(50)]
[Required]
public string dateStatus { get; set; }
[Required]
public bool isVisit { get; set; }
[Required]
public bool hasAvailableSlots { get; set; }
[Required]
public bool hasInterviewsScheduled { get; set; }
// navigation properties
public ICollection<ScheduledSchool> scheduledSchool { get; set; }
public ICollection<Interview> interviews { get; set; }
public ICollection<PartialDayAvailableBlock> partialDayAvailableBlocks { get; set; }
public Visit visit { get; set; }
public ICollection<Event> events { get; set; }
}
and
public class Visit
{
[Key]
public long flaggedDateScheduledDayID { get; set; }
[Required]
public bool isFullDay { get; set; }
// navigation property
public FlaggedDate flaggedDate { get; set; }
}
The relationship between these two is 1 : 0|1 -- i.e., FlaggedDate will exist but it may or may not have a corresponding single Visit object.
EF thinks, based on this model, that FlaggedDate should have an extra field, visit_flaggedDateScheduledDayID, which is nullable. I finally realized why: it thinks the Visit field, flaggedDateScheduledDayID, is an identity column. It's not supposed to be an identity column; it's supposed to be a foreign key that connects to FlaggedDate.
I think it does this by convention: I remember reading something to the effect that in CTP4, any field that is a single key and is int or long is assumed to be an identity column.
Is there any way I can tell EF that this is NOT an identity column? I tried fiddling with the Fluent API, but it's a mystery to me, and there are no data annotations that you can use for this.
Or, alternatively, is there any way I can fiddle with the navigation properties to get this to come out right?

If you're using mapping files with fluent API
this.Property(t => t.Id)
.HasColumnName("Site_ID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
I would imagine it should also work declaratively
[HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)]
although I didn't try that.

I discovered I can override the identity behavior with this code:
modelBuilder.Entity<Visit>().Property(v => v.flaggedDateScheduledDayID).StoreGeneratedPattern = System.Data.Metadata.Edm.StoreGeneratedPattern.None;
However, it is still not making it a foreign key. I guess that's a different question, though. It seems setting the StoreGeneratedPattern to None is the way to override the default behavior.

Related

Two optional one-to-one EF relationship

Is it possible to build two optional one-to-one relationship in SQL?
I'd like to have:
public class EventInvoice
{
[Key]
public int ID { get; set; }
[ForeignKey("SZ_Event")]
public Nullable<int> SZ_EventID { get; set; }
public virtual SzopbudkaEvent SZ_Event { get; set; }
[ForeignKey("UP_Event")]
public Nullable<int> UP_EventID { get; set; }
public virtual Event UP_Event { get; set; }
}
public class Event
{
[Key]
public int EventID { get; set; }
public virtual EventInvoice EventInvoice { get; set; }
}
public class SzopbudkaEvent
{
[Key]
public int ID { get; set; }
public virtual EventInvoice EventInvoice { get; set; }
}
My invoice can be combined only with one of those objects (Event or SzopbudkaEvent). Is it possible to use it like this or I have to write something different?
You can do this but there are two things to bear in mond.
If the constraint is only one of the FK's can exist, then in the database the FK columns on the EventInvoice tale must be nullable. You've got this but I thought I'd emphasise it.
If there is also a constraint that there must be one of them (missing both is not allowed) then you have to work out how to validate that constraint. In the DB I'd use a trigger fir insert, update that raises an exception if both are null. I'd match that in code with a pre-save check: this describes implementing interface IValidatableObject with a Validate method which EF will call when the object is affected by SaveChanges.

Fluent API mapping other side if a 1-1 relationship

I have a relationship set up between 2 tables using code first and Fluent API. This works and the schema is generated with the correct fields and key assignments. However, my problem is I need to be able to say SupplyPoint.SupplyPointPricing in my resultant Model. What I have below only gives me the other way around.
I figure there must be a way to keep the structure I have but just MAP SupplyPoint.
public partial class SupplyPoint
{
[Key]
public int SupplyPointId { get; set; }
public string SupplyPointName { get; set; }
}
public class SupplyPointPricing
{
public int SupplyPointPricingId { get; set; }
public int? SupplyPointId { get; set; }
[ForeignKey("SupplyPointId")]
public virtual SupplyPoint SupplyPoint { get; set; }
}
Then I use Fluent API and this gives me the 1-1 between the tables and the Schema I expect
modelBuilder.Entity<SupplyPointPricing>()
.HasOptional(a => a.SupplyPoint)
.WithMany()
.HasForeignKey(u => u.SupplyPointId);
Last time I had this problem I had to change the design around and have a foreign key in the SupplyPoint table. On that previous occasion that was OK since it was a 1-1 required where there are always a matching record. This time around I need to keep this structure since there is 1-0 between SupplyPoint and SupplyPointPricing.
This is how I have always done 1-to-1 relationships with my models, without using Fluent mapping:
public partial class SupplyPoint
{
[Key]
public int SupplyPointId { get; set; }
public virtual SupplyPointPricing SupplyPointPricing { get; set; }
}
public class SupplyPointPricing
{
[Key, ForeignKey("SupplyPoint")]
public int SupplyPointId { get; set; }
public virtual SupplyPoint SupplyPoint { get; set; }
}
As you can see, the SupplyPointPricing does not have an Id of its own, but only the Id of the SupplyPoint, which acts as a key in the 1-to-1 relatioship.
The only thing I'm not sure of is if the SupplyPointId can be a nullable int.
This will not create a foreign key on the SupplyPoint, so if you don't have a Pricing, the SupplyPoint.SupplyPointPricing property will be null.

Validation occurred client side with breeze entities for some properties which will be filled server side in the BeforeSaveEntity

I use the Durandal template in my asp.net mvc solution. The question here is related to Breeze which is also used in Durandal. Let's say I have the following entity in my model:
public class Driver
{
[Key]
public int Id { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
public string Comment { get; set; }
public int? CreatedById { get; set; }
public DateTime? CreatedTime { get; set; }
public int? UpdatedById { get; set; }
public DateTime? UpdatedTime { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User UpdatedBy { get; set; }
}
As you can see, I have some properties used to track creation/updates for time and userid (UpdatedById, UpdatedTime, ...). I would like to let the user edit/create my drivers in some data entry pages then fill in these properties (UpdatedById, UpdatedTime, ...) server side automatically in the BeforeSaveEntity method.
It works but as you noted I had to allow nullable on the properties like int? or DateTime? because in case of adding a new entity (everything is blank) the validation failed if I didn't proceed like that.
My question: is there another solution or something that could be done to avoid using nullable types on my model (int? - DateTime?) for these properties which track my creation/edition?
Thanks.
Make them nonnullable and fill in "dummy" values of the client, in a "registered" ctor for each type that will then get overwritten on the server.

Entity relationship not working

public class Slider_Locale
{
[Key]
public int Slider_LocaleID { get; set; }
[ForeignKey("Culture")]
public int CultureID { get; set; }
public string Slogan { get; set; }
public virtual Culture Culture { get; set; }
}
public class Culture
{
[Key]
public int CultureID { get; set; }
public string CultureName { get; set; }
public string DisplayName { get; set; }
public virtual Slider_Locale slider_Locale { get; set; }
}
It gives error as follows:
One or more validation errors were detected during model generation:
System.Data.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role
'Slider_Locale_Culture_Source' in relationship
'Slider_Locale_Culture'. Because the Dependent Role properties are not
the key properties, the upper bound of the multiplicity of the
Dependent Role must be �*�.
How could I design the relationship?. Please help me as I am newbie in mvc and entity.
This is one of those things that's a little tricky to wrap your brain around at first. The issue is that you're trying to set up a 1:1 (or 1:0) mapping, but there's nothing in your model to enforce that kind of mapping. For example, what if you have more than one Slider_Locale object with the same CultureID value? How would your application know which one to pick?
Now, you might know that this will never happen, but the Entity Framework doesn't, and it has to err on the side of caution, so it won't let you set up a relationship that it can't prove is consistent with the table structure. Ideally, it would let you specify unique constraints other than a primary key to work around this, and maybe someday it will, but for now the simplest way around this is to change it to a one-to-many mapping. For example, you could do:
public class Slider_Locale
{
[Key]
public int Slider_LocaleID { get; set; }
[ForeignKey("Culture")]
public int CultureID { get; set; }
public string Slogan { get; set; }
public virtual Culture Culture { get; set; }
}
public class Culture
{
[Key]
public int CultureID { get; set; }
public string CultureName { get; set; }
public string DisplayName { get; set; }
// Note that this got changed to ICollection<>
public virtual ICollection<Slider_Locale> slider_Locales { get; set; }
}
Another thing you could do is change the classes so that they share the same primary key values, but in order to do that you'll have to make at least one of the relationships optional. I could give an example of this if you let me know whether Slider_Locale.Culture can be null, or Culture.slider_Locale, or both.

MVC3 model instead model ... can't see when posting to controller

I have an order model (shown below)
public class Order
{
//[Key]
[ScaffoldColumn(false)]
public int OrderId { get; set; }
[DisplayName("Order Date")]
public DateTime OrderDate { get; set; }
public virtual ProductSelection ProductSelection { get; set; }
public virtual ShippingDetails ShippingDetails { get; set; }
public virtual BillingDetails BillingDetails { get; set; }
public virtual CardDetails CardDetails { get; set; }
public virtual AccountUser AccountUsers { get; set; }
}
As you can see is made up of a set of other models for example ProductSelection (shown below).
public class ProductSelection
{
public int SimulatorId { get; set; }
public string VersionNumber { get; set; }
[DisplayName("Quantity")]
public int Quantity { get; set; }
[DisplayName("Total Price")]
[ScaffoldColumn(false)]
public decimal TotalPrice { get; set; }
}
The issue I am having is when I post to the Controller which has a parameter of Order, I am unable to obtain any of the values from the sub-models (for example Order.ProductSelection.SimulatorId.)
Any ideas why this isn't working as I having to currently use FormCollection which isn't ideal and better messy.
Looking forward to replies
Steve
1) Silly question but just to make sure....Do you preserve values of your sub model on the view(In the form as hidden or any other input type,make sure name of your hidden are same as your properties name in the model) or in the query string.
Before giving you fully loaded model, model binder looks at different places to load your model like your form collection,rout data and query string
If you are not preserving them in any of these places then model binder has no way to find those values and give you loaded values on controller action.
Basics.. http://dotnetslackers.com/articles/aspnet/Understanding-ASP-NET-MVC-Model-Binding.aspx
2)Your example model seems fine but make sure all properties of your sub model have public access modifier and they must have set in their property declaration.
--->I had same issue before because I had private access modifier for set on those properties and I wasted whole day to figure that out.
3)If nothing works(hope that's not the case) then at last you can write your own model binder.
Here is the good post if you decide to head in that direction
http://buildstarted.com/2010/09/12/custom-model-binders-in-mvc-3-with-imodelbinder/
This is my first post (under my account) and it feels really good to participate..!!
You should apply ForeignAttribute on the ProductSelection property which points the primary key of the ProductSelection class:
[ForeignKey("SimulatorId")]
public virtual ProductSelection ProductSelection { get; set; }
Hope that helps.

Resources