EF6.1 Unique contraints over multiple columns - entity-framework

Consider the following class
public class Foo
{
[Key]
public int Id { get; set; }
[Required]
public virtual User User { get; set; }
[Required]
[MaxLength(20)]
public string Token { get; set; }
}
I need to combine User and Token into a unique constraint in the table. I thought this would be possible using the attribute [Index(IsUnique=true)]
I was testing this by first applying the attribute only to the Token property and then only to the User property. On the Token property I got the expected exception when adding duplicate records however on the User property I did not.
The user property is a foreign key (in this case an in) into the user table but this attribute wasn't enforcing uniqueness. Any idea why?

I stumbled across the answer through trial and error:
I needed to explicitly declare the foreign key for User rather than letting EF work it out. Then I can add the Index attribute to the foreign key property.
So rather than trying to do this:
[Required]
[Index("UserToken", 1, IsUnique = true)]
public virtual User User { get; set; }
I had to do this:
[Required]
[ForeignKey("UserId")]
public virtual User User { get; set; }
[Index("UserToken", 1, IsUnique = true)]
public int UserId { get; set; }

Related

Foreign Key Relationships with Entity Framework Code First (EntityData Issue)

I have two entity models, an Account and User and I am having difficulties implement foreign keys in the dependant model (User). As I am developing an Azure Mobile Service app I need to use the Entity Data interface which provides an 'Id' key field by default.
public class Account : EntityData
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AccountId { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string EmailAddress { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string SecurityQuestion { get; set; }
[Required]
public string SecurityAnswer { get; set; }
[Required]
public bool IsBusiness { get; set; }
public virtual User User { get; set; }
public virtual Business Business { get; set; }
}
public class User : EntityData
{
[Key, Column(Order=1)]
public virtual string Id { get; set; }
[Key, Column(Order=2), ForeignKey("Account")]
public int AccountId { get; set; }
public int UserId { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Surname { get; set; }
public virtual Account Account { get; set; }
}
My issue occurs when I specify I want to find 'AccountId' Entity Framework interprets it as 'Account' table, 'Id' column.
Output from Code Migrations:-
User_Account_Source: : Multiplicity is not valid in Role
'User_Account_Source' in relationship 'User_Account'. Because the
Dependent Role properties are not the key properties, the upper bound
of the multiplicity of the Dependent Role must be '*'.
User_Account_Target_User_Account_Source: : The types of all properties
in the Dependent Role of a referential constraint must be the same as
the corresponding property types in the Principal Role. The type of
property 'AccountId' on entity 'User' does not match the type of
property 'Id' on entity 'Account' in the referential constraint
'User_Account'.
Any insight would be highly appreciated!
The reason why EF understands it is one-to-many relationship instead one-to-one is because you are composing your PKs with the Id property, wich is not a FK.In one-to-one relationships one end must be principal and second end must be dependent. Principal end is the one which will be inserted first and which can exist without the dependent one. Dependent end is the one which must be inserted after the principal because it has foreign key to the principal. When configuring one-to-one relationships, Entity Framework requires that the primary key of the dependent also be the foreign key, otherwise EF doesn't see it as one-to-one relation.
public class Account
{
[Key]
public int Id { get; set; }
public virtual User User{ get; set; }
}
public class User
{
[Key, ForeignKey("Account")]
public int AccountId { get; set; }
public virtual Account Account{ get; set; }
}
If you think about that, it makes sense,otherwise, the below records could happen:
Accounts
Id
11111111
22222222
Users
Id AccountId
12rr 11111111
22tt 11111111

Entity Framework self referencing entity

I have a problem with the Entity Framework.
public class User : Receiver
{
public User()
{
if (Groups == null)
Groups = new List<Group>();
if (Buddies == null)
Buddies = new List<User>();
}
[Required]
public string PhoneNumber { get; set; }
[ForeignKey("Guid"), JsonIgnore]
public IList<User> Buddies { get; set; }
[ForeignKey("Guid"), JsonIgnore]
public IList<Group> Groups { get; set; }
}
public class Receiver
{
public Receiver()
{
Guid = Guid.NewGuid();
Created = DateTime.Now;
}
[Key]
public Guid Guid { get; set; }
[Required]
public DateTime Created { get; set; }
}
When i try to add a user...
User user = new User
{
Guid = new Guid("8cd094c9-e4df-494e-b991-5cf5cc03d6e3"),
PhoneNumber = "+4991276460"
};
cmc.Receivers.Add(user);
... it ends in follogwing error.
The object of the Type "System.Collections.Generic.List`1[Project.Models.User]" can't be converted to "Project.Models.User".
When i comment out following two lines:
[ForeignKey("Guid"), JsonIgnore]
public IList<User> Buddies { get; set; }
...the programm runs fine.
I hope someone can help me to fix this problem.
Otherwise it runs into an error at this line : cmc.Receivers.Add(user);
In your mapping...
[ForeignKey("Guid"), JsonIgnore]
public IList<User> Buddies { get; set; }
...you specify that User.Buddies is part of a one-to-many relationship and that User.Guid (=Receiver.Guid) is the foreign key in this relationship. But User.Guid is also the primary key, hence it must be unique. As a result a User cannot have a list of Buddies but only a single reference.
The mapping makes no sense but the exception is not very helpful and difficult to understand. (Somehow EF seems to recognize internally that the Buddies cannot be a list with that mapping and wants to cast the list to a single reference. It should detect in my opinion that the mapping is invalid in the first place.)
For a correct one-to-many mapping you need a foreign key that is different from the primary key. You can achieve that by either removing the [ForeignKey] annotation altogether...
[JsonIgnore]
public IList<User> Buddies { get; set; }
...in which case EF will create a default foreign key in the Receivers table (it will be some column with an underscore in its name, but you can rename that with Fluent API if you don't like the default name) or by adding your own foreign key property to the User class:
public Guid? BuddyGuid { get; set; }
[ForeignKey("BuddyGuid"), JsonIgnore]
public IList<User> Buddies { get; set; }

EF 4.1 code first with one-to-many relationship creates duplicate foreign keys

I am using entity framework code first. I have 2 entities (Users and Profile) and the relationship between them is one-to-many, that is, one user can only have one profile, but one profile can be assigned to many users. Below the entities:
[Table("Users")]
public class User
{
[Key(), Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public virtual string Name { get; set; }
[Required]
[ForeignKey("Profile")]
public virtual int ProfileId { get; set; }
public virtual Profile Profile { get; set; }
public virtual ICollection<AnotherEntityB> anotherEntityB { get; set; }
}
[Table("Profiles")]
public class Profile
{
[Key(), Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public virtual string Name { get; set; }
// Below the user that performs the discharge of the profile. Only 1 user can do it.
[ForeignKey("User")]
public virtual int? UserId { get; set; }
public virtual User User { get; set; }
public virtual DateTime? dischargeDate { get; set; } <-- this is the date that user performs the discharge of the profile
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<AnotherEntityC> anotherEntityC { get; set; }
}
also I have removed some conventions in the OnModelCreating method:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
The problem is that EF is creating two foreign keys in user entity:
ProfileId (FK, int, No NULL)
Profile_Id (FK, int, NULL)
and only one foreign key should be in Users entity:
ProfileId (FK, int, No NULL)
What's wrong?
Because there are two navigation properties User and Users in Profile that refer to the User entity EF cannot decide by a convention which of the two belongs to the inverse property Profile in entity User. You must EF give a hint using the [InverseProperty] attribute:
[InverseProperty("Users")]
public virtual Profile Profile { get; set; }
Now, it defines that User.Profile is the inverse navigation property of Profile.Users and that both are ends of the same relationship. Without the attribute EF assumes that the two navigation properties are ends of two different relationships and one of them is responsible for the additional foreign key Profile_Id.
Here is a bit more background.

DBContext simplest way to update foreign key

var orgAcc = db_.Accounts.Find(account.Id);
db_.Entry(orgAcc).CurrentValues.SetValues(account);
orgAcc.Company = db_.Companys.Find(account.Company.Id);
db_.SaveChanges();
Is this the simplest way to update an entity's association ?
public class ChartofAccount: MasterData
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(6)]
public string Code { get; set; }
public virtual Company Company { get; set; }
[Required]
public string AccountName { get; set; }
[Required]
[StringLength(3)]
public string AccountCurrency { get; set; }
public virtual AccountCatagory Category1 { get; set; }
public virtual AccountCatagory Category2 { get; set; }
public string Reference { get; set; }
public bool HasTransaction { get; set; }
}
The way SetValues works is to do a property-by-property compare, and for each property from the left-hand object that is also in the argument, that has a matching type, it will update the left-hand object with the value from the argument.
I presume account.Company is a different type of object to orgAcc.Company, such as something that has come in from an MVC controller argument (ie account and it's referenced objects are not EF entities). In this case your approach seems a sound way of doing it.
That being said, orgAcc probably has a Company property, and a CompanyId property, in order to support the EF relationships, so, if your account object followed the same pattern, ie storing a CompanyId field directly, rather than having to navigate through the company, then SetValues could automatically update the CompanyId field, which should update the foreign key when you save changes. This way you could also avoid the step that specifically assigns the orgAcc.Company field.

EF CTP4 Missing columns in generated table

I'm having an issue that i just can't seem to figure out. Lets say I have 2 Entities defined in my domain; Person and Document. Below is the definition for Document :
public class Document
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Title { get; set; }
public DateTime Date { get; set; }
public virtual Person Owner{ get; set; }
public virtual Person AssignedTo { get; set; }
}
Now, when EF CTP4 creates the SQL table on initialize, there is only one field mapping to a Person.Id being Owner_id. Whatever i try, the field for AssignedTo is never created.
Anything that could solve this?
Regards,
avsomeren
Your code perfectly created the desired schema in the database for me:
If you don't get this schema in you DB then my guess is that something is not right with the rest of your object model. Could you post your full object model please?
Another Solution:
While your current Document class will give you the desired results, but you can still take advantage of the Conventions for Code First and explicitly specify the FKs for your navigation properties:
public class Document
{
public int Id { get; set; }
[Required][StringLength(255)]
public string Title { get; set; }
public DateTime Date { get; set; }
public int OwnerID { get; set; }
public int AssignedToID { get; set; }
public virtual Person Owner { get; set; }
public virtual Person AssignedTo { get; set; }
}
Code First will now infer that any property named <navigation property name><primary key property name> (e.g. OwnerID), with the same data type as the primary key (int), represents a foreign key for the relationship.
This essentially results to the same DB schema plus you have the FKs on your Document object as well as navigation properties which gives you ultimate flexibility to work with your model.