This is my first time here so sorry if I did not put the doubt by default. In my project I have two models class doubts and a class of works, I want to create a classification for both using a superclass, like the design pattern strategy. But I have a problem, the project state is advanced and I have used the doubt and works classes many times. Already tried in many ways, but either the error in the update-database or the error in what I had done before.
Does anyone know one way that I can implement a generic class classification for the doubts and works classes without many changes of what I had already done?
My code is below
public abstract class Classificable
{
[Key]
public int id { get; set; }
public virtual Classification classication { get; set; }
}
public class Doubt : Classificable
{
public int doubtID { get; set; }
public string question { get; set; }
public string content { get; set; }
public virtual Student student { get; set; }
public virtual Course course { get; set; }
public virtual Work work { get; set; }
public virtual ICollection<Answer> answers { get; set; }
}
public class Work : Classificable
{
public int workID { get; set; }
public string name { get; set; }
public string nameWork { get; set; }
public string filePath { get; set; }
public virtual Student student { get; set; }
public virtual Course course { get; set; }
public virtual ICollection<Doubt> doubts { get; set; }
}
public class DB_DIS : DbContext
{
public DB_DIS()
: base("name=DB_DIS")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Doubt>().ToTable("Doubts");
modelBuilder.Entity<Work>().ToTable("Works");
}
public virtual DbSet<Doubt> Doubts { get; set; }
public virtual DbSet<Work> Works { get; set; }
}`
What is the property or method in Classificable that you are trying to apply to Doubt and Work?
Have you written your data structure in such a way that is contains an id field AND workID feild?
In my experience, if you are trying to share properties between EF Classes you are better off sharing common fields. I've used it in the past for base classes of AuditableBase as follows:
public class AuditableBase
{
public string UpdateUserId { get; set; }
public DateTime UpdateDate { get; set; }
}
Then all my classes that I want to "Audit" will have a UpdateUserId and UpdateDate, and I can do some pre-save processing to set those based on the type of AuditableBase.
Related
I'm trying to think of a way to store actual templates of ticket items in my Entity Framework MVC project. The thing is, I've already done a Code First migration process in the past. What I need to do is create logic in my code to allow someone to save time creating a ticket by using pre-loaded data from a template. My current inheritance model uses an abstract class (MasterTicket) which is used as the parent since to me there can be multiple categories (a Google Calendar based task, "Appointment Task" and a purely internal task, "General Task"). Here's my parent abstract class:
[Table("Ticket")]
[ModelBinder(typeof(MasterTicketBinder))]
public abstract class MasterTicket
{
[Key]
public Guid id{ get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ART { get; set; }
public DateTime openTime{ get; set; }
public DateTime? closeTime { get; set; }
private bool active = true;
public bool Active{ get => active; set => active = value; }
public string summary{ get; set; }
public string description{ get; set; }
public DateTime updateTime{ get; set; }
//TODO: Create foreign key relationship to user model
public Guid userUpdateId{ get; set; }
//TODO: Create foreign key relationship for tickets from other systems
public Guid externalAppId{ get; set; }
//TODO: Create foreign key relationship to user model
public Guid userOpenId{ get; set; }
public Guid? userCloseId { get; set; }
public Guid userOwnerId{ get; set; }
private int timesUpdated = 0;
public int TimesUpdated { get => timesUpdated; set => timesUpdated = value; }
public DateTime expectedCompletionTime{ get; set; }
public DateTime actualCompletionTime{ get; set; }
public List<MasterTicketItem> masterTicketItems{ get; set; }
public MasterTicket()
{
}
}
An here's an example of the concrete Google Calendar-based "Appointment Task" child:
[Table("AppointmentTickets")]
public class ApptTaskTicket : MasterTicket
{
public DateTime currentApptTime { get; set; }
public DateTime? endApptTime { get; set; }
public bool allDay { get; set; }
public string customerName { get; set; }
//TODO: Create foreign relationship
public Guid subjectPrsnlId { get; set; }
public string gCalEventId { get; set; }
public string customerPhone { get; set; }
public string customerEmail { get; set; }
public string preferredContactMethod { get; set; }
public List<ApptConfirmItem> apptConfirmItems { get; set; }
}
I know I can easily create a column for the MasterTicket class to indicate that it's a template, but to me I feel it's cleaner to have a separate "Template Table" if you will that will store pre-existing values that can be filled in with a form. In this case, I think I would WANT to create a duplicate class that would store said templates so that there are only several rows. What would be the best way to do this with Code First? Does someone feel I should take a different approach? Maybe DB First is a better way to go?
In case it matters, here's my DBContext for the Tickets:
// Code-Based Configuration and Dependency resolution
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class TicketDB : DbContext
{
public TicketDB(): base("AffirmativeServiceSystem.Properties.Settings.AffirmTaskManager")
{
}
public DbSet<MasterTicket> tickets { get; set; }
public DbSet<MasterTicketItem> ticketItems { get; set; }
}
I have 2 models which have exactly same fields, but I chose to make different models for them because I needed two different tables, one for each.
Earlier everything was working fine when I had two different tables for each model, but then I started using abstract base class because the code inside both the models were same.
Now I have a single table comprised of all the data that I save.
How can I create different tables for those two models.
public abstract class baseGrammar
{
[Key]
public int Id { get; set; }
[Required]
public string question { get; set; }
[Required]
public string ans { get; set; }
public string ruleId { get; set; }
public string ruleApplicable { get; set; }
[ForeignKey("ruleId")]
public virtual ruleTable RuleTable { get; set; }
}
The one shown above is my abstract base class.
public class article : baseGrammar
{
}
public class adjective : baseGrammar
{
}
Just if someone intrested in ruleTable model.
public class ruleTable
{
[Key]
public string ruleId { get; set; }
public string topic { get; set; }
public string rule { get; set; }
public string example { get; set; }
public virtual ICollection<baseGrammar> BaseGrammar { get; set; }
}
Am also adding context class so as to provide better description
public class english : DbContext
{
public english() : base("name=localServerEng")
{
Database.SetInitializer<DbContext>(null);
Database.SetInitializer<english>(new UniDBInitializer<english>());
}
public virtual DbSet<adjective> adjectiveDb { get; set; }
public virtual DbSet<adverb> adverbDb { get; set; }
public virtual DbSet<alternativeVerb> alternativeVerbDb { get; set; }
public virtual DbSet<antonyms> antonymsDb { get; set; }
public virtual DbSet<article> articleDb { get; set; }
private class UniDBInitializer<T> : DropCreateDatabaseIfModelChanges<english>
{
}
public System.Data.Entity.DbSet<StructureSSC.Areas.AreaEnglish.Models.baseGrammar> baseGrammars { get; set; }
}
Screenshot of SQL Server showing 1 table comprising of all columns instead of different tables
This set up will give you 2 tables: (1) adjectives (2) articles
The context should be like this:
public class SomeContext : DbContext
{
public SomeContext()
: base("name=SomeContext")
{
}
public virtual DbSet<article> Articles { get; set; }
public virtual DbSet<adjective> Adjectives { get; set; }
}
public abstract class baseGrammar
{
//... common properties/columns
}
public class article : baseGrammar
{
}
public class adjective : baseGrammar
{
}
Please note the naming convention. In .NET class names and property names should follow Pascal Notation. Therefore, they should be:
BaseGrammar
Article
Adjective
RuleApplicable // other properties should follow same convention
I have the following requirement, on my app the Entities will come with some fields, however the user needs to be able to add additional fields to the entity and then values for those fields.
I was thinking something like this but I am not sure if it would be a good approach or not.
The base class is an entity (Not sure which fields I need to add here)
public class Entidad
{
}
Then the Company Class will inherit from Entity
public class Empresa : Entidad
{
[Key]
public int Id { get; set; }
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
As you can see there is an ICollection of additional fields. that class would have the fieldname, type and id
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public Tiposcampo TipoCampo { get; set; }
}
and then the field value would be something like this:
public class ValorCampo
{
public Entidad Entidad { get; set; }
public CampoAdicional Campo { get; set; }
public string ValorTexto { get;set ; }
public int ValorNumerico { get; set; }
}
However I am not sure if this is the correct model classes for my scenario and whether it would create the tables correctly.
EF works with lazy load so at least there are several "virtual" missings.
In all properties that does not use primitive types and in collections.
Can you extend more than one entity with additional fields? If so you need that ValorCampo contains the entity (Entidad) but the entity should have the Id so you need to move the Id from Empresa to Entidad. Otherwise you need ValorCampo should refer to Empresa not to Entidad
Using code first, I have some abstract classes and some classes derived from those abstracted classes.
// Abstracted Classes
public abstract class Brand
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class Model
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
// Derived Classes
[Table("ComparisonBrand")]
public class ComparisonBrand : Brand
{
public ComparisonBrand()
{
ComparisonValues = new List<ComparisonValue>();
Models = new List<ComparisonModel>();
}
public virtual ICollection<ComparisonValue> ComparisonValues { get; set; }
public virtual ICollection<ComparisonModel> Models { get; set; }
}
[Table("ComparisonModel")]
public class ComparisonModel : Model
{
public int? BrandId { get; set; }
public int? LogoId { get; set; }
[ForeignKey("BrandId")]
public virtual ComparisonBrand ComparisonBrand { get; set; }
[ForeignKey("LogoId")]
public virtual ComparisonLogo ComparisonBrand { get; set; }
public virtual ICollection<ComparisonValue> ComparisonValues { get; set; }
}
My issue is that the migration generates foreign keys for:
ComparisonModel.Id > Models.Id
ComparisonModel.BrandId > Brands.Id
ComparisonModel.BrandId > ComparisonBrand.Id
Since ComparisonBrand.Id is a FK to Brands.BrandId, I get an error when deleting a Brand record. If I delete the ComparisonModel.BrandId > ComparisonBrand.Id relationship, however, the delete works fine.
How can I prevent a relationship from being formed between both the abstracted table and the derived table (Brands and ComparisonBrand)?
You are using the virtual keyword this causes Lazy Loading. You are telling EF to generate Foreign keys for them through this feature. Drop the virtual and you will not create the keys any longer
I'm using EF Code First (4.3.1) on a personal ASP.NET MVC 3 project, with a very simple domain model, and I'm almost at the point where EF will generate the DB schema the way I want it to.
The domain model has two classes: Painting and Gallery. Each Painting belongs to a single Gallery, and the Gallery has two virtual properties pointing to Painting: One to indicate which of the painting is it's cover image, and one for which of the paintings is the Slider image displayed on the home page.
The classes are as follow. I've removed some annotations and irrelevant properties to make it readable.
public class Gallery
{
public Gallery()
{
Paintings = new List<Painting>();
}
[ScaffoldColumn(false)]
[Key]
public int GalleryId { get; set; }
public string Name { get; set; }
[ScaffoldColumn(false)]
[Column("LaCover")]
public Painting Cover { get; set; }
[ScaffoldColumn(false)]
[Column("ElSlider")]
public Painting Slider { get; set; }
[ScaffoldColumn(false)]
public virtual List<Painting> Paintings { get; set; }
}
and painting:
public class Painting
{
[ScaffoldColumn(false)]
[Key]
public int PaintingId { get; set; }
public string Name { get; set; }
public int GalleryId { get; set; }
[Column("GalleryId")]
[ForeignKey("GalleryId")]
[InverseProperty("Paintings")]
public virtual Gallery Gallery { get; set; }
public string Filename { get; set; }
}
It generates a correct db schema for both classes and its relationships, the only small issue I have is that I haven't found a way to control the column names it gives to the virtual properties of Cover and Slider in the Gallery table.
It'll name them Cover_PaintingId and Slider_PaintingId.
I tried using the [Column("columnNameHere")] attribute, but that doesn't affect it at all. As in "I typed a certain non related word and it didnt show up in the schema".
I'd like to name it CoverPaintingId, without the underscore.
Any help is greatly appreciated. Thanks
The ForeignKey attribute lets you define the property that will act as your foreign key if you want it to exist within your model:
[ForeignKey("CoverPaintingId")]
public virtual Painting Cover { get; set; }
public int? CoverPaintingId { get; set; }
Note you can put the attribute either on the virtual property on the foreign key - just need to specify the name of the "other one".
However, since you will have two relationships between the same set of entities, you won't be able to do this without disabling Cascading deletes on one or both of them. This can only be done using the Fluent Configuration API.
public class Gallery
{
public int GalleryId { get; set; }
[ForeignKey("CoverPaintingId")]
public virtual Painting Cover { get; set; }
public int? CoverPaintingId { get; set; }
[ForeignKey("SliderPaintingId")]
public virtual Painting Slider { get; set; }
public int? SliderPaintingId { get; set; }
}
public class Painting
{
public int PaintingId { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Gallery> Galleries { get; set; }
public DbSet<Painting> Paintins { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Gallery>().HasOptional(g => g.Cover).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Gallery>().HasOptional(g => g.Slider).WithMany().WillCascadeOnDelete(false);
}
}
If you don't want the foreign key properties to exist in your code model, you can also configure these by using .Map(...) before the .WillCascadeOnDelete(false) part of the API instead of using ForeignKey. I prefer to use Foreign Key, but here's how the code would look if you wanted to do it this way:
public class Gallery
{
public int GalleryId { get; set; }
public virtual Painting Cover { get; set; }
public virtual Painting Slider { get; set; }
}
public class Painting
{
public int PaintingId { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Gallery> Galleries { get; set; }
public DbSet<Painting> Paintins { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Gallery>().HasOptional(g => g.Cover).WithMany().Map(m => m.MapKey("CoverPaintingId")).WillCascadeOnDelete(false);
modelBuilder.Entity<Gallery>().HasOptional(g => g.Slider).WithMany().Map(m => m.MapKey("SliderPaintingId")).WillCascadeOnDelete(false);
}
}
you could try to use [inverseproperty] decoration to achieve this.