Entity Framework enum property is ignored by code first - c#

Okay, the problem is this. I have multiple models that make use of enum properties, in all but one of them I've had no problem with this. In this model however code first does not recognise this property and will not create a column in the respective table. What's bizarre is that I have a very similar model to the non-functioning one that works fine, the only difference being that it's in a different namespace.
I'll add that:
My project is targeting .NET 4.5 (and indeed the functioning and non-functioning models are within the same project).
I'm using Entity Framework 5
My enums are defined publicly in the namespace, not nested within a class.
This eliminates the most common causes of problems when trying to use enums in EF.
Here are the code snippets, first the non-working one:
public enum Commodities
{
Test1,
Test2,
Test3
}
[Table("Suppliers")]
public class Suppliers
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Required]
[MaxLength(7)]
[Display(Name = "Vendor ID")]
public string VendorID { get; set; }
[Required]
[Display(Name = "Supplier Name")]
public string SupplierName { get; set; }
public Commodities Commodity { get; set; }
public bool Visible { get; set; }
}
Now here is one that works fine, only difference being that it's in a different namespace and class file (both sit in the same project and have identical using declarations):
public enum Commodities
{
Test4,
Test5,
Test6
}
[Table("Buyers")]
public class Buyer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Buyer ID")]
[Required]
public string BuyerID { get; set; }
[Display(Name = "Buyer Name")]
[Required]
public string Name { get; set; }
[Display(Name = "Windows Logon ID")]
public string WinUserID { get; set; }
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Please enter a valid e-mail adress")]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
public Commodities Commodity { get; set; }
[Display(Name = "Active?")]
public Boolean IsActive { get; set; }
}
In the "Buyer" table code first correctly initialising the Commodity column and maps it to the enum in the model. In the "Suppliers" table however it's only initialised with the columns: VendorID, VendorName, and Visible with Commodity no where to be found. My question is why is this happening and what can I do to fix it? I'm at my wits end with this.
EDIT: Seems the issue might have to do with using two enums with the same name. Although the compiler has no complaints about ambiguity (and it shouldn't as they exist in different namespaces) it seems EF doesn't like it. Changing the name of one of the enums causes CF to recognise the property.

Related

Necessity of navigation properties in EF Core models

Let's say in our project we use C# and MsSQL and we have one Products table with two columns (ID,Name)
One day we decided to save product information given by Company1, so we created a new table ProductInfoFromCompany1 because it has custom columns (ProductID, Price, CurrentScore)
The next day, we agreed with Company2 and now we need to save their data as well. So, new table -> ProductInfoFromCompany2 with different columns (ProductID, Year, Rating)
Another day, we agreed with Company3 and so on...
So, we have no idea how the data given by new companies will look like. That's why we need to create a new table because if we use one Details table, it will be too wide with numerous null columns
In Entity Framework Core we have these models:
public class ProductInfoFromCompany1
{
public int Id { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
public double CurrentScore { get; set; }
public Product Product { get; set; }
}
public class ProductInfoFromCompany2
{
public int Id { get; set; }
public int ProductId { get; set; }
public int Year { get; set; }
public double Rating { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
//Do we need these navigation properties in this class?
//public ProductInfoFromCompany1 ProductInfoFromCompany1 { get; set; }
//public ProductInfoFromCompany2 ProductInfoFromCompany2 { get; set; }
}
You can see my question is commented in the Product class.
Do we need to add navigation properties in the Product class?
The reason why I'm asking is that in all books or documentation which I've read, people use navigation property, but in this case, it violates open-closed principle because whenever we add new company, we need to modify Product class as well.
P.S. if we want to query ProductInfoFromCompany1 data and we have product Id, we can simply start querying from ProductInfoFromCompany1, like this
var info = _db.ProductInfoesFromCompany1.Where(c=>c.ProductId == productId);
Do we need to add navigation properties in the Product class?
You are the only one who can answer the question if you need something or not.
If the question is does EF Core require navigation properties, the answer is no. Reference: Relationships - Single Navigation Property EF Core documentation topic:
Including just one navigation property (no inverse navigation, and no foreign key property) is enough to have a relationship defined by convention.
In fact EF Core fluent API and shadow properties allow defining relationship without any navigation or FK property. How useful it would be is another story. The main point (which is the question as I read it) is that none of them is mandatory.
Of course the lack of a navigation property imposes some limitations on the type of LINQ queries you can create - like you said, you can't start a query from Product and apply filter on associated ProductInfoFromCompany1, or eager/explicit/lazy load it.
But if you don't need all that, e.g. as you said, you can build your queries starting from ProductInfoFromCompany1, then omitting the navigation property in Product is perfectly fine.
As I mentioned in my comment a design change is required to achieve what you want.
Here is my suggestion:
Since your issue is with the structure of the product table because you don't know what each company wants to store as info for their product you can do it this way : (I ll explain later).
public class Company
{
[Key]
public int Id { get; set; }
[Display(Name = "Name")]
[Required]
public string Name { get; set; }
[Display(Name = "Description")]
public string Description { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
public virtual ICollection<Product> Prodcuts { get; set; }
}
public class Product
{
[Key]
public int Id { get; set; }
[Display(Name="Name")]
[Required]
public string Name { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
[Required]
[ForeignKey("Company")]
[Display(Name = "Company")]
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<ProductField> Fields { get; set; }
}
public class ProductField
{
[Key]
public int Id { get; set; }
[Display(Name = "Value")]
[Required]
public string Value { get; set; }
[Required]
[ForeignKey("Product")]
[Display(Name = "Product")]
public int ProductID { get; set; }
public virtual Product Product { get; set; }
[Required]
[ForeignKey("Field")]
[Display(Name = "Field")]
public int FieldID { get; set; }
public virtual Field Field { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
}
public class Field
{
[Key]
public int ID { get; set; }
[MaxLength(100)]
[Index("ActiveAndUnique", 1, IsUnique = true)]
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Description")]
public string Description { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
}
Explanation of the code:
This approach gives you more control over your data without having to create a table for each product info.
Company:
I started by creating a company table with a navigation property that will lazy load all the products related to it.(if lazy loading is enabled)
Then In the product table I added a FK to reference the company.
Field:
Since you mentioned that you don't know what a company will have as product info , you can create a new field and link it to a product using the ProductField table .
ProductField:
This table will act as a "Many to Many" between your product, and field as a result you can add as many field to a new product without having to modify the structure of your product table or create a new one . You can also reuse the same field if company number 3 needs it.
USAGE:
Given we have a company named MyCompany.
MyCompany has a product named Car and the info required to be added to the car is Make, and Color.
We create two new fields called Make, and Color, then in the ProductField Table we add two new entries:
The first one will have:
The ID of the field "Make", The value "BMW", and a reference to the product with its id which is Car.
We do the same thing for color by referencing the the field "Color" and the product "Car".
Querying:
Now querying is simpler than having a table for each company product info.
Example:
var myProducts = _db.Products.Where(p=>p.CompanyID== "1").Include(p=>p.Fields).Tolist()
Again that's my take on it. Hope it helps.

Incorrect String Length in Entity Framework

I'm using EF6 (Code First) in a project.
By having below class:
public class State
{
public int ID { get; set; }
[Required]
[StringLength(10)]
public string Code { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
public Country Country { get; set; }
}
I expect to have Code as nvarchar(10) in database but I get nvarchar(3). I see the correct length for Name column but can't figure out why Code is not created correctly.
Edit:
I have the Country class as below:
public class Country
{
[Key]
[StringLength(3)]
public string Code { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
}
I think, EF thinks the Code in State class is the Code in Country class as there is association between them.
Now, question is how should I tell EF that the Code in State class is not the Foreign Key to Country class?
Use MaxLength instead, EF will decide how large to make a string value field when it creates the database.
StringLength is data annotation, that will used to validate user input.
MSDN:
MaxLength - Specifies the maximum length of array or string data allowed in a property.
StringLength - Specifies the minimum and maximum length of characters that are allowed in a data field.
Since Question Updated:
Use [ForeignKey("CountryCode")] atribute, change your Code in Country class to CountryCode (or whatever you prefer) and specify your column name by Column["Code"] attribute:
public class State
{
public int ID { get; set; }
[Required]
[StringLength(10)]
public string Code { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[ForeignKey("CountryCode")]
public Country Country { get; set; }
}
public class Country
{
[Key]
[StringLength(3)]
[Column["Code"]]
public string CountryCode { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
}
MSDN Links: Column Attribute, ForeignKey Attribute
Or just change your Codes to StateCode and CountryCode and use [ForeignKey("CountryCode")] attribute.
Even after working with it for a long time, EF is still surprising me. Until now I was thinking that by default EF is searching for property named {Navigation Property Name}{Referenced Entity PK Property Name} as default explicit FK property. But with your sample (verified), seems like it also does the same for property named {Referenced Entity PK Property Name}.
Since ForeignKey attribute cannot be used to specify the table column name (it can only specify FK/navigation property name), if you want to keep the model classes exactly as they are, you should use the MapKey fluent configuration, for instance:
modelBuilder.Entity<State>()
.HasRequired(s => s.Country)
.WithMany(s => s.States)
.Map(s => s.MapKey("CountryCode"));
Since you are referring to another class in the State model it will create a foreign key for you based on the name of the properties , so to avoid making the EF decide what the name of the column for you add the following in the state class :
public string CountryId { get; set; }
if you want to choose another name other than CountryId , suppose you want to change it to CountryForeignKey you can use the following:
using System.ComponentModel.DataAnnotations.Schema;
.
.
.
[ForeignKey("CountryForeignKey")]
public Country Country { get; set; }
public string CountryForeignKey { get; set; }
and this is what you get in the database

Duplicate entries in one to many relation

I have two models as seen below.
public class UserModel
{
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Display(Name = "User Name")]
[StringLength(15, MinimumLength = 8, ErrorMessage = "Length Should not be less than 8 characters")]
[Required]
public string UserName { get; set; }
[Display(Name = "QuestionCategory")]
public virtual ICollection<QuestionCategoryModel> QuestionCategoryModel { get; set; }
}
and
public class QuestionCategoryModel
{
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
[Display(Name = "Title")]
[StringLength(15, MinimumLength = 2, ErrorMessage = "Length Should not be less than 2 characters")]
public string Title { get; set; }
[Required]
public virtual UserModel User { get; set; }
[Required]
public bool IsDeleted { get; set; } = false;
}
The issue am running into is, when i try to insert data to QuestionCategory.
_DbInstance.QuestionCategories.Add(category);
_DbInstance.SaveChanges();
The issue is, each time am adding QuestionCategories, a new row gets added in User despite data already being there. Could someone guide me ?
update
Category variable is coming to a call to Controller, however, am attaching the User component fetching the value from session.
Category.User = (UserModel)Session["User"];
_QCategories.SaveCategory(Mapper.Map<Degree360.ORM.Dictionary.QuestionCategoryModel>(Category));
The problem here is that you don't attach the user to the context your context here is _DbInstance you can follow this link it will provide more information
Entity Framework Add and Attach and Entity States.
I guess you have a DbSet of UserModel that i will call here Users for my example.
so the solution will be something like that.
_DbInstance.Users.Attach(category.User)
PS : you have to do it before the SaveChanges()

Alternative to multiple inheritance in Entity Framework

I would like to use inheritance to enforce consistency in Entity Framework model classes. For example, if X different models all have an address, they could inherit from:
public abstract class EntityAddress
{
[MaxLength(400)]
[Display(Name = "Street address")]
[DataMember]
public string AddressLine1 { get; set; }
[MaxLength(400)]
[Display(Name = "Address line 2")]
[DataMember]
public string AddressLine2 { get; set; }
[MaxLength(100)]
[Display(Name = "City")]
[DataMember]
public string City { get; set; }
[MaxLength(100)]
[Display(Name = "State")]
[DataMember]
public string State { get; set; }
[MaxLength(40)]
[Display(Name = "Zip code")]
[DataType(DataType.PostalCode)]
[DataMember]
public string ZipCode { get; set; }
}
This would ensure that all addresses are consistently implemented across the product (yes, if a model has two addresses, we have an issue, but I'll wave that away for the purposes of this discussion).
I would also like the ability to have a class use an unlimited number of these concepts. For example, if a model has an address and a full name, it could do this:
public class Customer : EntityAddress, EntityFullName
{
}
Multiple inheritance, however, is not supported in C#.
Does anyone have any ideas on good alternate methods to achieve what I am trying to do here? I don't believe interfaces will work because I can't embed the attributes with the properties. I don't believe a class property will work because I want the columns in the DB associated with the base classes to be in the same table as the model class properties.
Complex types appear to be an answer to this question (credit to Ivan Stoev).
https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-2-complex-types
You could wrap in a class. Its not pretty, but it will achieve similar results
public class Customer
{
public EntityAddress address {get;set;}
public EntityFullName fullname {get;set;}
}

List of bytes in MVC model not generated in database by EF code first

I'm developing MVC 4 application using EF code first and data anotations,
I have created a class which contains following properties:
public class Product
{
[Key]
[ScaffoldColumn(false)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID {get; set; }
[Required]
[MaxLength(20, ErrorMessage = "Max 20 chars!")]
[Display(Name = "name of product")]
public string Name
[Required]
[Display(Name = "List of items included into product name")]
public List<byte> ProductsNameCompositionList { get; set; }
}
ProductsNameCompositionList property actually reffers to enum:
public enum ProductNameCompositionElement : byte { CategoryName, PropertyNames, PropertyNamesWithValues }
when I start application all field of Product model are generated in database automatically,
except one - public List ProductsNameCompositionList not being created ....
Am I doing something wrong?
I don't think EF knows how to map List<byte>. I would try just byte[].
[Required]
[Display(Name = "List of items included into product name")]
public byte[] ProductsNameCompositionList { get; set; }

Categories