Abstract Factory Mapping EF Core .NET 6 - c#

I have tremendous doubts.
I'm using Abstract Factory in my payment system:
public abstract class Payment : Entity
{
protected Payment(DateTime paidDate, DateTime expiredDate, decimal total, decimal totalPaid, string payer, Document document, Address address, Email email)
{
Number = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 10).ToUpper();
PaidDate = paidDate;
ExpiredDate = expiredDate;
Total = total;
TotalPaid = totalPaid;
Payer = payer;
Document = document;
Address = address;
Email = email;
AddNotifications(new Contract<Payment>()
.Requires()
.IsLowerOrEqualsThan(0, Total, "Payment.Total", "O total não pode ser zero")
.IsGreaterOrEqualsThan(Total, TotalPaid, "Payment.Total", "O Valor pago é menor que o valor do pagamento")
);
}
public string Number { get; private set; }
public DateTime PaidDate { get; private set; }
public DateTime ExpiredDate { get; private set; }
public decimal Total { get; private set; }
public decimal TotalPaid { get; private set; }
public string Payer { get; private set; }
public Document Document { get; private set; }
public Address Address { get; private set; }
public Email Email { get; private set; }
}
public class CreditCardPayment : Payment
{
public CreditCardPayment(string cardHolderName, string cardNumber, string lastTransactionNumber, DateTime paidDate, DateTime expiredDate, decimal total, decimal totalPaid, string payer, Document document, Address address, Email email) : base(paidDate, expiredDate, total, totalPaid, payer, document, address, email)
{
CardHolderName = cardHolderName;
CardNumber = cardNumber;
LastTransactionNumber = lastTransactionNumber;
}
public string CardHolderName { get; private set; }
public string CardNumber { get; private set; }
public string LastTransactionNumber { get; private set; }
}
My real doubt is how do I map this inside EF Core in the DbSet part
because when I try to map the class to be implemented by the abstract, it gives an error when I upload the dotnet ef migrations add
public DbSet<CreditCardPayment> creditCardPayment{ get; set; }
No suitable constructor was found for entity type 'CreditCardPayment'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'document', 'address', 'email' in 'CreditCardPayment(string cardHolderName, string cardNumber, string lastTransactionNumber, DateTime paidDate, DateTime expiredDate, decimal total, decimal totalPaid, string payer, Document document, Address address, Email email)'.
He brings me this
would i have to do
public DbSet<Payment> Payments{ get; set; }
also?
in this case, what is the best way to implement the Abstract Factory pattern in the EF CORE
in this case, what is the best way to implement the Abstract Factory pattern in the EF CORE

EF requires an empty constructor, always, so you need to either add a public or a protected empty constructor for when the DbSet is created it woul dbe able to set properties accordingly.
Your CreditCardPayment is also not passing the properties to the base class. One thing you could do is change to something like this: (I'll omit a few things for brevity)
public abstract class Payment : Entity
{
protected Payment() { }
public Payment(DateTime paidDate, DateTime expiredDate, decimal total, decimal totalPaid, string payer, Document document, Address address, Email email)
{
// all properties here
}
}
public class CreditCardPayment : Payment
{
protected CreditCardPayment() { }
public CreditCardPayment(DateTime paidDate, DateTime expiredDate, decimal total, decimal totalPaid, string payer, Document document, Address address, Email email)
: base(paidDate, expiredDate, total, totalPaid, payer, document, address, email)
{
// the other 3 properties from credit card payment
}
}
One other option is to map payment as a owned type instead of using inheritance. That would give you a navigation property instead and the mapping is quite different as well. The rule of having an empty constructor still applies.
public class CreditCardPaymentConfiguration : IEntityTypeConfiguration<CreditCardPayment>
{
public void Configure(EntityTypeBuilder<CreditCardPayment> builder)
{
builder
.OwnsOne(x => x.PaymentInfo);
}
}
public class Payment
{
}
public class CreditCardPayment
{
public string PropertyX { get; set; }
public Payment PaymentInfo { get; set; }
}
Same strategy for Address and Email for example.

Related

c# Automapper mapping string to 1st element of array in nested object

My repository is returning back a user object which has this definition where the user will only ever have 1 email address.
User
public class User
{
[Key]
public string UserName { get; set; }
[Required]
[MaxLength(50)]
public string DisplayName { get; set; }
[Required]
[MaxLength(50)]
public string Email { get; set; }
}
Then I am mapping that to a UserDTO object for transmission to an environment where they expect the Email field to be an array of Email objects. So I created this Email object per what the receiving system needs and it looks like this below. We could set Type to a string with a value of "work" and Primary to boolean true;
public class Email
{
public string Value { get; set; }
public string Type { get; set; }
public bool Primary { get; set; }
}
And then I have my UserDTO that looks like this:
public class UserReadDto
{
public string schemas { get; set; }
public string UserName { get; set; }
public string externalId { get; set; }
// this should be an array of names, this is a name object.
public Name name { get; set; }
public string DisplayName { get; set; }
public Email[] Emails { get; set; }
}
Is it possible to have Automapper map the email string, such as test#test.com to an Email object array that only has one Email object in it for the destination?
You can create a function that returns the list for you, something like this:
public static Email[] GetList(User x)
{
return new List<Email>
{
new Email()
{
Value = x.Address
}
}.ToArray()
}
And then you can put this in your mapping configuration:
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserReadDto>()
.ForMember(d => d.Emails, src =>
{
src.MapFrom(x => GetList(x));
});
});
You can put the GetList() method inside your User model, or anywhere else really, as long as you can access it in your mapping configuration.
More info on the docs page of automapper here.

Model Validation using Fluent Validation on WEB API

I am trying to use fluent validation for my models on Web API project. I have two classes name OrderHeader and Items . And i have property name as OrderQty on Items class which is an integer and i have applied a rule for OrderQty as it should be number only (i.e. 0-9) . whenever i get the JSON request for OrderQty as alphanumeric (like 1A) i cannot serialize the JSON and could not get the errormessage from fluent validation on Modelstate . How to achieve this could someone help me on this please ? Thanks in advance !!!
I have tried to convert the OrderQty to ToString() and applied rule but i could not get the errormessage while serialiaing the JSON .
My modal classes :
public class OrderHeader
{
public string OrderNumber { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Phone { get; set; }
public string Created { get; set; }
public List<Items> Items { get; set; }
}
public class Items
{
public string ItemNumber { get; set; }
public string Description { get; set; }
public int OrderQty { get; set; }
public double Weight { get; set; }
public double Price { get; set; }
public string Status { get; set; }
}
Fluent Validations
public class OrderHeaderValidator : AbstractValidator<OrderHeader>
{
public OrderHeaderValidator()
{
RuleFor(x => x.OrderNumber.Trim()).NotEmpty().WithMessage("OrderNumber : cannot be blank.").Length(1, 6).WithMessage("OrderNumber : cannot be more than 6 characters.").Matches("^[0-9]*$").WithMessage("OrderNumber : must contains only numbers");
RuleFor(x => x.Items).SetCollectionValidator(new ItemValidator());
}
}
public class ItemsValidator : AbstractValidator<Items>
{
public ItemsValidator()
{
RuleFor(x => x.OrderQty.ToString()).NotNull().WithMessage("TotalOrderQuantity : cannot be blank").Matches("^[0-9]*$").WithMessage("TotalOrderQuantity : must contains only numbers");
RuleFor(x => x.Status.ToUpper()).NotEmpty().WithMessage("Status : Provide the Status").Length(0, 1).WithMessage("Status : cannot be more than 1 character").Matches("O").WithMessage("Status : Must be 'O'");
}
}
Serializing and getting error message :
string errors = JsonConvert.SerializeObject(ModelState.Values
.SelectMany(state => state.Errors)
.Select(error => error.ErrorMessage));
I expect the output should be if the value for is 1A from JSON request then it displays the error message as:
TotalOrderQuantity : must contains only numbers
You can't deserialize 1A into int OrderQty. Use string OrderQty instead and check .Must(x => int.TryParse(x.OrderQty, out _)) in validator.
It's OK that your API has specification like OrderQty must be integer - int OrderQty. If someone tries to send string instead of integer - you can catch deserializion exception and reject the request with message like invalid request: ...

How can I access the PayTime field which is inside 'paid' class in Linq

There is a field called FactorStateBase which we implemented using pattern strategy. This field has a default value of PendingFactorState. We wish to access all factors that in paid state and their dates fall within a specific time interval. This payment time interval is 'new'ed inside the PaidFactorState when the payment is actually done and the new class is assigned to the state field.
My question is how can I access the payment date field which is inside 'paid' class in Linq?
Thank you,
public class Factor : AggregateRoot<long>, IHasCreationTime
{
public Factor(long id, long orderId, decimal price)
{
Id = id;
Price = price;
OrderId = orderId;
FactorStateBase = new PendingFactorState(Guid.NewGuid());
}
public long OrderId { get; private set; }
public decimal Price { get; private set; }
public virtual FactorStateBase FactorStateBase { get; private set; }
public void PayFactor(Guid id, string messageNumber, Guid paymentId, string transactionCode)
{
FactorStateBase = new PaidFactorState(id, messageNumber, paymentId, transactionCode);
}
}
public abstract class FactorStateBase : Entity
{
protected FactorStateBase(Guid id)
{
Id = id;
}
}
public class PaidFactorState : FactorStateBase
{
public PaidFactorState(Guid id, string messageNumber, Guid paymentId, string transactionCode) : base(id)
{
PayTime = DateTime.Now;
MessageNumber = messageNumber;
PaymentId = paymentId;
TransactionCode = transactionCode;
}
public DateTime PayTime { get; private set; }
public string MessageNumber { get; private set; }
public Guid PaymentId { get; private set; }
public string TransactionCode { get; private set; }
}
factors=_repository.AsQueryable.Where(p=>p.FactorStateBase is PaidFactorState && p.PayDate > = yourdate)
you can use is for this code and for paid filed you use PendingFactorState field

Should we user Request and Response model same?

As a good API writing practice should we use request and response model separate or common?
Ex.
public abstract class BaseModel
{
public DateTime CreatedOn {get;set;}
public int CreatedOn {get;set;}
}
public class UserModel : BaseModel
{
public int UserId {get;set;}
public string UserName{get;set;}
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
string _name;
public string Name
{
get { return string.Format("{0}{1}{2}", FirstName, MiddleName, LastName) }
set { _name=value; }
}
}
Now if I'm creating API
CreateUser(UserModel uModel)
{
}
GetUsers()
{
// Here I'm returning all users from the system, so each user's object will contains CreatedOn and CreatedBy, which I don't require in the API response
}
So here should we need to create multiple UserModel ?
If your response format is json, one solution is using [ScriptIgnore] annotation such that :
public abstract class BaseModel
{
[ScriptIgnore]
public DateTime CreatedOn { get; set; }
[ScriptIgnore]
public int CreatedOn2 { get; set; }
}
So that you can use your common class with not serializing unwanted fields for your api.

Hide some public properties in ServiceStack service Request and Response DTO's

I am building some services using ServiceStack, I have a following service
public class FooRequest: IReturn<FooResponse>
{
public int FooID { get; set; }
public decimal Amount { get; set; }
public string SortColumnName { get; set; }
}
public class FooResponse
{
private List<Payment> payments;
public FooResponse()
{
payments= new List<Payment>();
}
public List<Payment> Payments
{
get { return payments; }
set { payments = value; }
}
}
public class Payment
{
public int ID { get; set; }
public string Amount { get; set; } // Amount is formatted string based on the user language
public decimal PaymentAmount{ get; set; } // this is Alias for Amount
}
From above FooResponse, I don't want to show PaymentAmount in response/metadata.
If we make it as normal variable instead of property, it won't be visible in metadata. but that should be a property for other requirement.
Is it possible in ServiceStack to restrict some properties in response DTO?
Use the IgnoreDataMember attribute on the PaymentAmount property. Last time I checked ServiceStack was using and complying to the default .NET serialization attributes.

Categories

Resources