Two one-to-many relationship with abstract class - c#

I'm not sure whether or not my logic and implementation is correct and won't have any issues. The potential problem is the following: I have an abstract class, Classifier, which has two concrete classes: ToolClassifier and ManualClassifier:
[Table("Classifiers")] //TPT Inheritance
public abstract class Classifier
{
[Column(name: "classifier_id")]
public int Id { get; set; }
[Column(name: "date_added")]
public DateTime DateAdded { get; set; }
public ICollection<ClassifierVulnWeight> ClassifierVulnWeight { get; set; } = null!;
}
[Table("ToolClassifiers")] //TPT Inheritance
public class ToolClassifier : Classifier
{
[Column(name: "tool_name")]
public string ToolName { get; set; } = null!;
[Column(name: "tool_acronym")]
public string ToolAcronym { get; set; } = null!;
[Column(name: "tool_url")]
public string ToolURL { get; set; } = null!;
}
[Table("ManualClassifiers")] //TPT Inheritance
public class ManualClassifier : Classifier
{
[Column(name: "first_name")]
public string FirstName { get; set; } = null!;
[Column(name: "label_name")]
public string LastName { get; set; } = null!;
[Column(name: "email")]
public string Email { get; set; } = null!;
[Column(name: "username")]
public string Username { get; set; } = null!;
[Column(name: "passwordHash")]
public string PasswordHash { get; set; } = null!;
[Column(name: "passwordSalt")]
public string PasswordSalt { get; set; } = null!;
}
The Classifier establishes a one-to-many relationship with the class ClassifierVulnWeight. The VulnerabilityClass also establishes a one-to-many relationship with the class ClassifierVulnWeight:
public class ClassifierVulnWeight
{
[Column(name: "classifier_id")]
public int ClassifierId { get; set; }
public Classifier Classifier { get; set; } = null!;
[Column(name: "vuln_class_id")]
public int VulnerabilityClassId { get; set; }
public VulnerabilityClass VulnerabilityClass { get; set; } = null!;
[Column(name: "last_update")]
public DateTime LastUpdate { get; set; }
[Column(name: "weight")]
public double Weight { get; set; }
}
public class VulnerabilityClass
{
[Column(name: "vuln_class_id")]
public int Id { get; set; }
[Column(name: "vuln_class_name")]
public string VulnerabilityClassName { get; set; } = null!;
[Column(name: "vuln_class_acronym")]
public string VulnerabilityClassAcronym { get; set; } = null!;
public ICollection<ClassifierVulnWeight> ClassifierVulnWeight { get; set; } = null!;
}
The ClassifierVulnWight has a composite primary key, composed of the classifier Id and VulnClass Id, as shown below:
modelBuilder.Entity<ClassifierVulnWeight>()
.HasKey(classifierVulnWeight => new
{
classifierVulnWeight.ClassifierId,
classifierVulnWeight.VulnerabilityClassId
});
My question is the following: will I have any troubles when creating instances of ClassifierVulnWeight since I have the abstract class Classifier? This is because the obligation is in the Tool Classifier and Manual Classifier entities, i.e., there is no Classifier instance unless there is at least an instance of one of the two aforementioned.
I'm guessing that the ICollection property in the Classifier instance is inherited by both children and thus there will be no problem. Is my assumption correct? Thank you in advance.

Related

c# converting object using DTOs

Good day,
I have these classes I populate from db:
public class BaseProduct
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int MainCategoryId { get; set; }
public MainCategory mainCategory { get; set; }
public int MaterialId { get; set; }
public Material material { get; set; }
public ICollection<ProductVariant> productVariants { get; set; }
public ICollection<ImageBase> ImageBases { get; set; }
public int UnitsSold { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal Price { get; set; }
public int Discount { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal TotalPrice { get; set; }
}
public class ImageBase
{
[Key]
[Column(TypeName = "bigint")]
public long Id { get; set; }
[Column(TypeName ="varchar(max)")]
public string ImagePath { get; set; }
[Column(TypeName ="datetime")]
public DateTime AddedOn { get; set; }
public int BaseProductId { get; set; }
[JsonIgnore]
public BaseProduct baseProduct { get; set; }
//TODO fix typo
public string SaticPath { get; set; }
}
I want to convert them into these:
public class BaseProductCustomReturn
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public MainCategory mainCategory { get; set; }
public Material material { get; set; }
public ICollection<ProductVariant> productVariants { get; set; }
public ICollection<BaseImageReturn> ImageBases { get; set; }
public int UnitsSold { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal Price { get; set; }
public int Discount { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal TotalPrice { get; set; }
}
public class BaseImageReturn
{
[Key]
[Column(TypeName = "bigint")]
public long Id { get; set; }
[Column(TypeName = "datetime")]
public DateTime AddedOn { get; set; }
public int BaseProductId { get; set; }
[JsonIgnore]
public BaseProduct baseProduct { get; set; }
//TODO fix typo
public string StaticPath { get; set; }
}
The main difference is in BaseProductCustomReturn I have Ienumerable(BaseImageReturn) instead of IEnumerable(ImageBase)
I wrote this dto conversion method
public static IEnumerable<BaseProductCustomReturn> ConvertToDto(this IEnumerable<BaseProduct> baseProducts)
{
var baseProductCustomReturn = (from baseProduct in baseProducts
select new BaseProductCustomReturn
{
Id = baseProduct.Id,
Name = baseProduct.Name,
Description = baseProduct.Description,
mainCategory = baseProduct.mainCategory,
material = baseProduct.material,
productVariants= baseProduct.productVariants,
ImageBases
});
}
As you can see I got stuck in converting ImageBase, how do I convert ImageBase to ImageBaseReturn? They ar both lists, so I cant convert One By One.
You could use LINQ
ImageBases = baseProduct.ImageBases.Select(b => new BaseImageReturn(){/* Copy properties */}).ToList()
Note that most of the benefit from DTO-types comes from allowing you to have a more complex model without needing to take serialization concerns into consideration. For example allowing you to have private setters, and methods that does validation etc. In your example your DTO and model are nearly identical, so you will not gain a large benefit.
If you are using databases you might also consider using a Object Relational Mapper (ORM) like Entity Framework (EF).

Map two lists to parent class and nested class

I have two lists, list of Accounts and list of Transactions that represent below model. Let's assume they look like this:
List<Response.Account> accounts;
List<Response.Transaction> transactions;
And the model:
public class Response
{
public List<Account> Accounts { get; set; } = new List<Account>();
public class Account
{
public string AccountNumber { get; set; }
public long AvailableCredit { get; set; }
public double Balance { get; set; }
public long? CertainDate { get; set; }
public string CredentialsId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string UserId { get; set; }
public bool Closed { get; set; }
public List<Transaction> Transactions { get; set; } = new List<Transaction>();
}
public class Transaction
{
public string AccountId { get; set; }
public long Amount { get; set; }
public string CategoryId { get; set; }
public string CredentialsId { get; set; }
public long Date { get; set; }
public string Description { get; set; }
public string FormattedDescription { get; set; }
public string Id { get; set; }
public long Inserted { get; set; }
public long LastModified { get; set; }
public long OriginalAmount { get; set; }
public long OriginalDate { get; set; }
public string OriginalDescription { get; set; }
public long Timestamp { get; set; }
public string Type { get; set; }
public string UserId { get; set; }
}
}
To each Account class in Response class I want to assign a list of transactions by mapping Id from Account class and AccountId from Transaction class. I am aware I could do that by iterating over Accounts or Transactions and checking if both ids are equal and then assign to proper collection but that would cause nested iterations and I am not sure about its efficiency. What should I do? The only contraints in this model are those two lists in the beggining.

Entity Framework shared POCO

I have a bit of an issue and I need to know what the best design is for EF.
I have a Attribute model:
public enum TargetType
{
Attributes,
Availability,
Master
}
public class Attribute
{
public int Id { get; set; }
public int CriteriaId { get; set; }
[Required, MaxLength(100)] public string Name { get; set; }
[MaxLength(100)] public string Description { get; set; }
public TargetType Target { get; set; }
public virtual IList<Formula> Formulas { get; set; }
}
It has a list of Formulas:
public class Formula
{
public int Id { get; set; }
public int AttributeId { get; set; }
[Required]
[MaxLength(5)]
public string Operator { get; set; }
[Required]
[MaxLength(255)]
public string Expression { get; set; }
[MaxLength(100)]
public string Field { get; set; }
}
Which I match up in my DbContext like this:
modelBuilder.Entity<Attribute>().HasMany(m => m.Formulas).WithOptional().HasForeignKey(m => m.AttributeId);
The problem is, I now need to have Formulas as a separate property to another class Answer like this:
public class Answer
{
public int Id { get; set; }
public int QuestionId { get; set; }
[Required]
[MaxLength(255)]
public string Text { get; set; }
public int Order { get; set; }
public virtual IList<Formula> Formulas { get; set; }
}
The problem with this is Formula has AttributeId as a foreign key, so I can't really use the Formula model. I would have to create a new one like:
public class AnswerFormula
{
public int Id { get; set; }
public int AnswerId { get; set; }
[Required]
[MaxLength(5)]
public string Operator { get; set; }
[Required]
[MaxLength(255)]
public string Expression { get; set; }
[MaxLength(100)]
public string Field { get; set; }
}
But this seems counter-intuitive. So my question is, has anyone come across this before? If so, how did you solve the issue? For me, the problem is further enhanced when I start to use view models and factories.

Code first - Configure Navigation Not Working - Entity Framework

I´m having a big problem to configure the navigation on my application.
These are the bases (but already with problems)
public class ModeloBase
{
[Key()]
public int Id { get; set; }
public int InseridoPorId { get; set; }
[Column(TypeName = "datetime2")]
public DateTime DataInserido { get; set; }
[Column(TypeName = "datetime2")]
public DateTime DataAtualizado { get; set; }
public bool Ativo { get; set; }
#region Navigation
[Required]
[ForeignKey("InseridoPorId")]
public virtual Colaborador InseridoPor { get; set; }
#endregion
}
public class Colaborador : ModeloBase
{
public Colaborador()
{
ColaboradoresSubordinadosPorMim = new HashSet<Colaborador>();
}
public int SuperiorId { get; set; }
[ForeignKey("SuperiorId")]
public Colaborador Superior { get; set; }
public ICollection<Colaborador> ColaboradoresSubordinadosPorMim { get; set; }
}
public class Contato : ModeloBase
{
[Required]
[StringLength(10)]
public string Endereco { get; set; }
}
Basically I have a base class "ModeloBase" that all extends it and I want that all thables have a FK to Colaborador with the Column Name "InseridoPorId"
As soon as I try to generate the controller it keep warning me with Multiplicity problems for these Navigation.
Any help?
Thanks.

ASP.Net MVC 5 EF6 - Allow nullables on two properties but force at least one to be present

In simple relational terms, I want each entry of ContractDetails to be assigned to either a Site OR a Company, not both at the same time, and one of them must be selected or there is no link at all. I'm not quite sure how to represent this in entity framework. My Model at Present:
Company Model:
public class Company
{
public int ID { get; set; }
public string Company_Name { get; set; }
public string Company_Prefix { get; set; }
public virtual ICollection<Site> Sites { get; set; }
}
Contract Details Model:
public class ContractDetails
{
public int ContractDetailsID { get; set; }
public int ContractTypeID { get; set; }
public int ContractRenewalPeriodID { get; set; }
public int? CompanyID { get; set; }
public int? SiteID { get; set; }
[Required, StringLength(10)]
public string Reference { get; set; }
[Display(Name = "Contract Start Date"), DataType(DataType.Date)]
public DateTime? Contract_Start_Date { get; set; }
[Display(Name = "Contract End Date"), DataType(DataType.Date)]
public DateTime? Contract_End_Date { get; set; }
[Column(TypeName = "text")]
public string Notes { get; set; }
public string Direct_Debit_Reference { get; set; }
public virtual Company Company { get; set; }
public virtual Site Site { get; set; }
public virtual ContractType ContractType { get; set; }
public virtual ContractRenewalPeriod ContractRenewalPeriod { get; set; }
}
Site Model:
public class Site
{
public int ID { get; set; }
public string Site_Name { get; set; }
public string Site_TelephoneNumber { get; set; }
public string Site_City { get; set; }
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
}
Just implement the IValidatableObject on ContractDetails class. On Validate method put the validation logic. If the object is not valid you must return a collection of ValidationResult. when saving the object, EF will execute the Validate method and verify that your ContractDetails object is coherent.

Categories

Resources