Entity Framework - Unique constraint on a string column - c#

I'm a beginner with EF (6.2) and I'm trying to generate a database using the code first approach.
Some of my entities have a string property which should be unique, like the name of a user or the title of an article.
In order to ensure unicity, I've added an index which specifies that the column should be unique :
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Blog.Models
{
public class User
{
[Key, Index, Required]
public int Id { get; set; }
[Index(IsUnique = true), Required]
public string Name { get; set; }
[Required]
public string Password { get; set; }
public ICollection<Comment> Comments { get; set; }
}
}
However it complains because I'm trying to add an index on a string type column, while it accepts using an index on an integer column, such as the row ID.
I could use the Key attribute, but I'm already using it to define a primary key and I don't want EF to beleive that I want the name to be a component of a composite primary key nor to consider it as the actual primary key.
So my question is : Why can't I add an index on a column of string type and how can I ensure the unicity (uniqueness ?) of a given column ? Thank you.

As written in the Tutorial from MSDN, Indexes are possible on strings as they state in the following example code
public class User
{
public int UserId { get; set; }
[Index(IsUnique = true)]
[StringLength(200)]
public string Username { get; set; }
public string DisplayName { get; set; }
}
but when it comes to Multiple-Column Indexes, they mention you need to write the order.
Probably there's a conflict arising because you use Index without any name on Id, and also an Index without any name on Name. Try defining a name for the Index of the Name. I am not sure if this works, but it's worth a try.
More info can be found here at the MSDN website.

Related

Foreign Key int Array C#

I have related table. How can I keep the relationship in this table as an array?
public int TreatmentId { get; set; }
[ForeignKey("TreatmentId")]
public virtual Treatment Treatment { get; set; }
I want to be able to give the TreatmentId here like this;
When I make an array and try to migrate, I get the following error;
The property 'ContactPage.TreatmentId' is of type 'int[]' which is not supported by the current database provider. Either change the property CLR type, or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
public int[] TreatmentId { get; set; }
[ForeignKey("TreatmentId")]
public virtual Treatment Treatment { get; set; }
You cannot save an integer array as FK, however I have seen people adding comma separated values as string, but this design is a very bad practice and will cause you many problems.
You need to have a third Table (one of your tables is Treatments, assuming the other one is Patients), like PatientsTreatments:
public class PatientsTreatments
{
public int PatientId { get; set;}
public int TreatmentId { get; set;}
}
and then you need to add a new record for each treatment.

ASP.NET MVC - create drop down list for a model that has composite primary key?

I'm trying to allow for selecting an item from list of items. The problem is that each item has a composite key, and if I use a drop down list, the value for each item is expected to be a string (but I need two strings for the composite key).
Right now, I've hacked it to combine the composite key into one string with a delimiting character in between when setting the list, and then parsing the value expecting the delimiting character when something is selected. Is there a cleaner/better way of achieving this? Obviously my current approach can fail if one of the keys already is using the same character.
Model:
public class CompositeKeyModel
{
[Key]
public string Name { get; set; }
[Key]
public string Group { get; set; }
}
How about you create a get property to get the Value of the Property to retrive the composite Key
public class CompositeKeyModel
{
[Key]
public string Name { get; set; }
[Key]
public string Group { get; set; }
public string CompositeKey {get
{
return Name+Group
}}
}

Requiring Unique Values in Entity Framework among items that share a common property value

I am trying to make the AnswerString required to be unique among answers that share the same ModuleID value. How would I go about achieving this?
public class Answer
{
public int AnswerID { get; set; }
[MaxLength(25)]
[Required]
[Key]
public string AnswerString { get; set; }
public int ModuleID { get; set; }
public int PictureCount { get; set; }
}
Add this attribute to AnswerString:
[Index("IX_AnswerStringModuleId",2,IsUnique = true)]
Then add this attribute to ModuleId:
[Index("IX_AnswerStringModuleId",1, IsUnique = true)]
Basically this sets up a unique constraint where the combo of ModuleId and AnswerString must be unique.
See this answer as well: Unique Key constraints for multiple columns in Entity Framework
I would recommend making an attribute for the property which would evaluate at the class or property level. This would apply your business logic within the object/model and not rely on EF. If your intent is not to do as such, then I recommend the attributes suggested above or applying a constraint on the table itself.

OrmLite Inserting 0 and instead of auto-incrementing primary key

I am trying to create a generic Insert<T> for our objects. I am new to OrmLite so I am still reading up on it. The objects that are used do not use an Id property they have a more detailed name.
As an example this a basic POCO:
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
...etc.
}
So the primary key is CustomerId and through some more reading I found that OrmLite likes to use the property Id for the primary keys. As we have a convention not to use just the name Id for the FK I cannot switch. However reading further it seemed like I could decorate the property with an attribute or two and get it to work.
This is what I am working with:
public class Customer
{
[AutoIncrement]
[Alias("CustomerId")]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
...
}
I get a SqlException stating the following:
Additional information: Cannot insert the value NULL into column 'CustomerId',
table 'XXX.dbo.Customer'; column does not allow nulls. INSERT fails
I did some more reading and thought I could fix the issue by inheriting from an interface.
public class Customer : IHasId<int>
{
[AutoIncrement]
[Alias("CustomerId")]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
...
}
I have played with using the PrimaryKey attribute and I still get the same result.
Has anyone had an issue like this? If you did how did you solve it? I am having a hard time finding some more information on the matter.
I can get rid of the attributes and name the property back to CustomerId so it matches the db table and it will insert into the table but it will always put in 0 as the key, which makes sense just because it is the default value for the int but does not help me when it has to be an autoincrementing primary key. As a side note I am using ServiceStack.OrmLite.SqlServer.3.9.71 and SQL Server 2008
UPDATE 1
So I went through the documentation again today for 3.9 version of ServiceStack.OrmLite and read through their description on what I should do when I don't have POCOs with an 'Id' property for the Primary Key. It is as follows:
... by convention OrmLite expects it to be Id although you can use [Alais("DbFieldName")] attribute to map it to a column with a different name or use the [PrimaryKey] attribute to tell OrmLite to use a different property for the primary key.
I used both of the examples and it does in fact insert my data to the SQLDatabase. However, it is still inserting 0 for the CustomerId primary key.
If I use the AutoIncrement attribute it throws a SqlException:
An exception of type 'System.Data.SqlClient.SqlException' occured in System.Data.dll but was not handled by user code. Additional Information: Cannot insert the value NULL into column 'CustomerId', table 'dbo.Customer'; column does not allow nulls. INSERT fails.
Has anyone run into this issue? I keep running into roadblocks.
i experimented the same issue. Your following code was already good.
public class Customer
{
[AutoIncrement]
[Alias("CustomerId")]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
...
}
The problem don't come from ORMLITE but from your database. Indeed, the column "CustomerId" which is i think the primary key for your table have his property "Identity" set to "False". You must set it to "True" or "Yes" and also set "Identity Increment" and "Identity Seed" to 1.
In v4.0.40, servicestack retrieves the primary key column by naming convention ("column_name" == OrmLiteConfig.IdField) as shown by the following code from OrmLiteConfigExtensions.cs:
internal static bool CheckForIdField(IEnumerable<PropertyInfo> objProperties)
{
// Not using Linq.Where() and manually iterating through objProperties just to avoid dependencies on System.Xml??
foreach (var objProperty in objProperties)
{
if (objProperty.Name != OrmLiteConfig.IdField) continue;
return true;
}
return false;
}
Therefore, using [AutoIncrement] with [Alias] should not work.

EF Code First Auto generate column ID

I was wondering if it where at all possible to have a Key that auto generated like this:
CBE_2012_1
CBE_2012_2
CBE_2012_3
CBE_2013_1
CBE_2013_2
CBA_2013_1
CBA_2013_2
CBC_2013_1
I now have this in my class, with ID is Id with autonumbering:
[Key]
public int Id { get; set; }
public DateTime Date { get; set; }
public string Code { get; set; }
public string Name { get; set; }
But I would like to have this with Number being the KEY & format: Code_Year_Id:
[Key]
public string Number { get; set; }
public DateTime Date { get; set; }
public string Code { get; set; }
public string Name { get; set; }
Can anybody help me with this?
Or does anybody have helpfull documentation that can provide me with a decent solution?
Before I save a new item I could just create a number myself (In code), but there could be 2 people saving a new item # the same time and I do not want an exception on duplicate key..
Zarkos
Generally, using a meaningful value for a key is a bad idea. You will be much better off using a sequential value who's only purpose is to ensure referential integrity. If for not other reason than: if your key is not sequential, you are going to get a lot of fragmentation on your index as you insert new records.
If you do want to be sure that you get a unique value, you could create a SQL table that holds your last value, and then a UDF or sproc that takes the current value in that table, increments it to the next value and then returns that value.

Categories

Resources