Cart
it ICartState
to define the ICartState
interface and declare one implementation for each state. In more detail, this approach is described in the text here and in the video format here . public enum BusinessEntityType { [Display(Name = ". : , LTD, ...")] LegalEntity = 0, [Display(Name = "")] IndividualEntrepreneur, [Display(Name = ". ")] Person, [Display(Name = " ")] NonProfitEducationalInstitution } [DisplayName("")] public class Contractor : NamedEntityBase { [Required] [Display(Name = " / .")] public Address MainAddress { get; set; } [Display(Name = " ")] public Address ActualAddress { get; set; } [Display(Name = " ")] public BusinessEntityType Type { get; set; } [Display(Name = ""), StringLength(127), RegularExpression("\\d+")] public string Inn { get; set; } [Display(Name = " ")] public virtual string Email { get; set; } [Display(Name = "")] public virtual string PhoneNumber { get; set; } public Contractor([NotNull] string name, BusinessEntityType type, [NotNull] Address mainAddress, Address actualAddress = null) : base(name) { if (mainAddress == null) throw new ArgumentNullException(nameof(mainAddress)); Type = type; MainAddress = mainAddress; ActualAddress = actualAddress; } protected Contractor() { } }
[DisplayName("")] [Table(nameof(Contractor), Schema = nameof(Office))] public class Contractor : NamedEntityBase { /// <summary> /// / . /// </summary> [Required] [Display(Name = " / .")] public Address MainAddress { get; set; } /// <summary> /// /// </summary> [Display(Name = " ")] public Address ActualAddress { get; set; } [Display(Name = " ")] public ContractorType Type { get; set; } [Display(Name = ""), StringLength(127), RegularExpression("\\d+")] public string Inn { get; set; } [Display(Name = " ")] public string Email { get; set; } [Display(Name = "")] public string Phone { get; set; } public string FullName => string.Join(" ", Name.Split(',')); public virtual LegalContact LegalContact { get; set; } public virtual PersonContact PersonContact { get; set; } public Contractor([NotNull] string name, ContractorType type, [NotNull]Address mainAddress, Address actualAddress = null) : base(name) { if (mainAddress == null) throw new ArgumentNullException(nameof(mainAddress)); Type = type; MainAddress = mainAddress; ActualAddress = actualAddress; } protected Contractor() { } } public class PersonContact : EntityBase, IContact { [Display(Name = ""), StringLength(127), RegularExpression("\\d+")] public string Inn { get; set; } public string Email { get; set; } public string Phone { get; set; } public string Name { get; set; } public string Surname { get; set; } public string Patronymic { get; set; } public Address Address { get; set; } } public class LegalContact : EntityBase, IContact { [Display(Name = ""), StringLength(127), RegularExpression("\\d+")] public string Inn { get; set; } public string Kpp { get; set; } public string Ogrn { get; set; } public string Okpo { get; set; } public string Okved { get; set; } public string Name { get; set; } public string FullName { get; set; } public string Email { get; set; } public string Phone { get; set; } public Address MainAddress { get; set; } public Address ActualAddress { get; set; } } public interface IContact : IHasId<int> { string Email { get; set; } }
IContact
interface raises questions. “Not so” here is that integrity control for LegalContact
and PersonContact
is possible only by using Check Constraint.LegalContact
and PersonContact
! Unfortunately, relational database management systems do not provide such functionality. In addition, the Type
field now looks redundant. Cases of IP and individual face are combined in PersonContact
, although from the point of view of business processes, IP caps are closer to legal entities.Contractor
class, heirs for each type of counterparty, and consider what unites them. The main use of the counterparty is the signing of contracts. In this case, we do not work with EDS, therefore, the full name and details of the parties are sufficient to substitute them into an automatically created document that will be printed and signed. [DisplayName("")] [Table(nameof(Contractor), Schema = nameof(Office))] public class Contractor : EntityBase { [Display(Name = " ")] public ContractorType Type { get; set; } [Required] public string Email { get; set; } public string Phone { get; set; } public Contractor(ContractorType type) { Type = type; } protected Contractor() { } } [Table(nameof(CompanyContractor), Schema = nameof(Office))] public class CompanyContractor : Contractor { [Display(Name = "/VAT"), StringLength(12)] public string Vat { get; set; } [Display(Name = ""), StringLength(9)] public string Kpp { get; set; } [Display(Name = ""), StringLength(13)] public string Ogrn { get; set; } [Display(Name = ""), StringLength(10)] public string Okpo { get; set; } [Display(Name = ""), StringLength(10)] public string Okved { get; set; } public string Name { get; set; } public string FullName { get; set; } public Address MainAddress { get; set; } public Address ActualAddress { get; set; } public CompanyContractor(string name) { Name = name; } protected CompanyContractor() { } } [Table(nameof(PersonContractor), Schema = nameof(Office))] public class PersonContractor : Contractor { [Display(Name = ""), StringLength(12)] public string Vat { get; set; } [Display(Name = " "), StringLength(15)] public string Ogrnip { get; set; } public string Name { get; set; } public string Surname { get; set; } public string Patronymic { get; set; } public Address Address { get; set; } public string FullName => string.Join(" ", Name, Surname, Patronymic); public PersonContractor(string name, string email) { Name = name; Email = email; } protected PersonContractor() { } }
Type
property, in case you need multiple dispatch . But following YAGNI we will leave everything as it is.ContractorBase
. Now the base class looks logical. The system is available details and representative of the counterparty. In case of questions you can try to contact by phone or e-mail.The question of how to determine that Ivan Ivanov Ivan from Kazan and Ivan Ivan Ivan from Penza - I will not consider different people, because this topic is worthy of a separate article.
IContractor
interface and IContractor
abstract methods there. Rename ContractorBase
to ContractorInfo
and make this class non-abstract to be able to display the list of suppliers. We return ContractorType
to distinguish the types of counterparties. EntityFramework materializes objects through Refletion, so we calmly leave the constructor and the Type
and Name
properties protected. Information about counterparties does not appear by itself, but is added only together with the counterparty itself. We had to denormalize the table, which is “not very” in terms of consistency and the need for additional gestures when changing the fields that are part of the “name”, but not bad for performance.Source: https://habr.com/ru/post/322168/
All Articles