Why database first classes differ from my code first classes - c#

I am migrating to SQL Server from MySql and re-writing a website in C# (I was/am a vb.net guy) using code-first.
I wrote the following class
namespace DomainClasses.GeographyDb
{
public class PostalCode
{
[Key]
public int PostalCodeId { get; set; }
[Required]
[DataType(DataType.PostalCode)]
public string PostCode { get; set; }
[Required, ForeignKey("City")]
public int CityId { get; set; }
[Required, ForeignKey("TimeZone")]
public int TimeZoneId { get; set; }
[Required, MaxLength(30)]
public string AreaRegionPhonePrefixCode { get; set; }
[MaxLength(10)]
public string TaxRegionCode { get; set; }
public virtual City City { get; set; }
public virtual TimeZone TimeZone { get; set; }
}
I wanted to see what Entity Framework would write if it were creating the class so I created a test project and using code first from database option I imported an existing database of exact same fields as I wrote the custom classes for.
Here is what it produced;
namespace CodeFirstCreationTest
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class postalcode
{
public long PostalCodeId { get; set; }
[Required]
[StringLength(10)]
public string PostCode { get; set; }
public long CityId { get; set; }
public long TimeZoneId { get; set; }
[Required]
[StringLength(10)]
public string AreaRegionPhonePrefixCode { get; set; }
[StringLength(10)]
public string TaxRegionCode { get; set; }
public virtual city city { get; set; }
public virtual timezone timezone { get; set; }
}
}
I have a number of questions as to the differences;
Why did Entity Framework put all the using statements INSIDE the namespace declaration? I thought they were always to be outside and before the code
StringLength is a data validation annotation. Why did Entity Framework put that attribute on the property, isn't it for UI validation? And now that I think of it I always put Required attribute on classes, is that a data validation annotation as well and not a database annotation?
It created a database class using fluent API and modelbuilder. Is it better to annotate the class properties or do it using the modelBuilder in the OnModelCreating method?
Why did Entity Framework make this class partial and apparently all the classes it generates as partial?

Using statements are sometimes generated inside the actual namespace declaration. Usually there is no difference. Only in some cases is it a bit different. Please see this answer for more info, since it is explained better than I would be able to: https://stackoverflow.com/a/151560/1757695
StringLength and Required are both database annotations and the according operations are executed right before saving the data in the database. For example if you were trying to save a User and the user had a UserName property decorated with Required data annotation, you would get an error only when you called SaveChanges method and not when you assigned the value to the property.
I believe it is personal preference. I prefer data annotations and as little as possible fluent API. There are still some things that you can't do with data annotations but can do with fluent API.

Related

Using Entity Framework Code-First, can I make my class generic and derive a store-able string field from the type?

My use-case:
I'd like to store a representation of a file tree in my local (SQLite) database using EF.
My model will be a simplified copy a much larger model on a remote SQL database (also in EF)
I'd like to use one, generic entity that self-refers to create a tree structure, and derives its 'type' field from one of the original entity types (FiletypeA, FiletypeB, Folder etc.. using the interface IFileSynchronisable)
I figured the best way was to make the class generic, and deriving a string field from the type using nameof(T) and Type.GetType("FiletypeA"), but I've got stuck trying to instantiate the class when building the model:
public class FileSyncObject<T> where T : class, IFileSynchronisable
{
[Key]
public Guid Id { get; set; }
public long ObjectId { get; set; }
//Can I derive T from some 'ObjectType' field in the record?
public string ObjectType { get { return nameof(T); } }
public long ProjectId { get; set; }
public string AmazonS3Path { get; set; }
public bool IsActive { get; set; }
public string Version { get; set; }
public Guid LocalParentId { get; set; }
public FileSyncObject<T> LocalParent { get; set; }
public virtual ICollection<FileSyncObject<T>> LocalChildren { get; set; }
}
What's the best approach? Is this even possible?

Specifying the column type when using entity framework core 3.1 with SQL Server

Consider this simple class, that I will use for one of my Domain objects with EF Core 3.1:
using System;
namespace Blah.Domain
{
public class Response
{
public int Id { get; set; }
public string FullResponseText { get; set; }
public string HttpResponseCode { get; set; }
public string TransactionId { get; set; }
public string TransactionType { get; set; }
public DateTime CreatedDate { get; set; }
}
}
Having a database background, I do not want to use the default type of nvarchar(max) as the column type for string values in my database (SQL Server). How do I specify the column types to be used by EF when creating the table?
Also, do I have to include the whole SYSTEM Namespace just to be able to have the DateTime option available to me for my CreatedDate field or is there another way?
Basically there are two possibilities for this problem. The one is to use the attributes mentioned in the previous answer or to use the fluent API provided by EF core.
Fluent API allows you to configure precisely your database properties.
More info can be found in the documentation
Basically the required code is the following in the database context
modelBuilder.Entity<Address>()
.Property(a => a.StateProvince).HasColumnType("varchar(20)");
You should be able to add an attribute on each string value which will look like this
[MaxLength(50)] //Whatever int value in `maxlength` will be the size in sql
public string FullResponseText { get; set; }
[MaxLength(255)]
public string HttpResponseCode { get; set; }
etc.....
Or you could use [StringLength(50, MinimumLength = 5)]
To use [MaxLength()] you will need System.Collections.Generic. For DateTime System should be the only namespace that you need.

Entity Model + Scalar Property + Custom Type

When using entity framework's model designer is it possible to add a property to an entity that is not one of the standard types?
I have these two entities. The one, VirusDescription I would like to add another property which is a class I wrote however when you go to change the type of the property it only gives you basics... i.e. strings, int16...etc. Is there a way to include custom types in the designer?
I can go into the code that the designer generates and just add it myself and everything works fine but I would like the code and the designer to be consistent.
Here is the class definition for the VirusDescription entity which I updated by hand. If there is a way to update the designer from the corresponding code that would work too.
namespace Trojan.Database
{
using System;
using System.Collections.Generic;
public partial class VirusDescriptionItems
{
public string ItemId { get; set; }
public string VirusId { get; set; }
public bool On_Off { get; set; }
public System.DateTime DateCreated { get; set; }
public short AttributeId { get; set; }
public short CategoryId { get; set; }
public virtual Attribute Attribute { get; set; } //Added
public virtual Category Category { get; set; } //Added
}
}
You can create a complex type within the entity model browser and extend the generated class using partial implementations.

Entity Framework Foreign Key DataAnnotations

I'm attempting to set up EF classes for a "code-first" approach, using the techniques outlined at the EF page at Data Developer Center, a related SO answer here, another SO thread here, and this article at CodeProject.com.
Two classes need to have one-to-many interaction using data annotations, specifically foreign keys.
Everything seems to be in order with my classes. I can perform a context.Add(), but when saving the changes through context.SaveChanges(), I get the following error message:
The ForeignKeyAttribute on property 'Machines' on type
'BacklogTracker.Models.EFModels.Customer' is not valid. The foreign
key name 'MachID' was not found on the dependent type.
Why am I getting this error?
My EF classes and foreign keys seem to be in order, based on the examples and techniques outlined in the links at the beginning of this question... but I can't figure out what's wrong. I'm a beginner at this, so it's very possible I'm missing something entirely.
I'm using VS2013 Express, .NET framework 4.5, EF 6.1.2. Here is code for the classes:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Customer
{
public Customer()
{
Machines = new List<Machine>();
}
[Key]
public int Number { get; set; }
public string Name { get; set; }
public string MachID { get; set; }
[ForeignKey("MachID")]
public virtual List<Machine> Machines { get; set; }
}
and
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Machine
{
public Machine()
{
Customer = new Customer();
}
[Key]
public string SN { get; set; }
public string Model { get; set; }
public int Hours { get; set; }
public string Location { get; set; }
public int CustID { get; set; }
[ForeignKey("CustID")]
public virtual Customer Customer { get; set; }
}
By your codes, I suppose to that the relationship between customer and machine is one-to-many. One customer has many machines, and a machine is owned by one customer.
The machines are grouped into customer by a foreign key CustID. For those machines with the same CustID, they are under the same customer. If customer has a column named MachID, it will mean that a machine has many customers. It will conflict the fact that one customer has many machines.
#Nathan is right about to remove those codes.
If the MachID should exist, then you need to answer a question about what relationship holds between customer and machine. Is it many-to-many, one-to-many or many-to-one ? If the relationship is many-to-many, then your codes are totally wrong.
public class Customer
{
[Key]
public int Number { get; set; }
// One customer has many machines, so the `MachID` SHOULD NOT exist.
// public string MachID { get; set; }
// [ForeignKey("MachID")]
public virtual List<Machine> Machines { get; set; }
}
public class Machine
{
[Key]
public string SN { get; set; }
...
public int CustID { get; set; }
[ForeignKey("CustID")]
public virtual Customer Customer { get; set; }
}
You should remove:
public string MachID { get; set; }
[ForeignKey("MachID")]
EF can infer the relationship from the machine side, if I understand you correctly
Also, shouldn't you have ID properties ?
It might be because you are annotating List rather than the Machine class itself. I'm not sure if relationships can be defined on a collection like that.

How to use AutoMapper with Entity Framework Database First approach

I am working on MVC 5, Entity Framework with Db First Approach. Whenever i use
public class Customer
{
[Required]
public virtual string CustomerID { get; set; }
[Required]
[StringLength(15)]
public virtual string CompanyName { get; set; }
public virtual string Address { get; set; }
public virtual string City { get; set; }
public virtual string PostalCode { get; set; }
[Country(AllowCountry="USA")]
public virtual string Country { get; set; }
[Phone]
public virtual string Phone { get; set; }
}
for Validation in Entity Framework generated class and if i update my .edmx file i lost all code that i was written. Someone suggest me use Auto-mapper. I try to find some basic example but i didn't get. Guide me. How to start and where from? I am new in MVC, Entity Framework.
There is another approach to retain the validation attribute code even if you update the model.
In the Models folder, add a class named Metadata.cs (class that contain all of the validation attributes).
using System;
using System.ComponentModel.DataAnnotations;
namespace YourProjectName.Models
{
public class CustomerMetadata
{
[Required]
public virtual int CustomerId { get; set; }
[Required]
[StringLength(15)]
public virtual string CompanyName { get; set; }
}
}
Next, you must associate the model classes with the metadata classes.For that In the Models folder, add a class named PartialClasses.cs.
using System;
using System.ComponentModel.DataAnnotations;
namespace YourProjectName.Models
{
[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{
}
}
Source

Categories

Resources