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).
Related
This is the first time I have attempted to join information from one database to the other. I have a accounting database and a regular site database. Trying to keep them separate. I have a page that shows Transactions but am getting the information for a few of the columns from the regular database by way of Id's. Below is my Model. I am showing nothing in the fields for the items in the other database.
public class Transaction
{
[Key]
public Guid TransactionId { get; set; }
public string Description { get; set; }
public int? CompanyId { get; set; }
public Guid? VendorId { get; set; }
public string InCheckNumber { get; set; }
public string OutCheckNumber { get; set; }
public string InvoiceNumber { get; set; }
public string PurchaseOrderNumber { get; set; }
public Guid LedgerAccountId { get; set; }
public decimal? DebitAmount { get; set; }
public decimal? CreditAmount { get; set; }
public DateTime TransactionDate { get; set; }
public string ModifiedBy { get; set; }
public DateTime? ModifiedDate { get; set; }
public string SavedDocument { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
public bool IsCredit { get; set; }
public bool IsDebit { get; set; }
public Guid Type { get; set; }
[ForeignKey("LedgerAccountId")]
public LedgerAccount LedgerAccount { get; set; }
[ForeignKey("CompanyId")]
public CompanyNames Company { get; set; }
[ForeignKey("VendorId")]
public Vendors Vendor { get; set; }
}
I have added the 'using' of the General.Entities to this model. Is there something else i need to add for this?
Thanks in advance.
UPDATE:
See Question - Link to answer of question
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.
I'm trying out OrmLite to see if I can replace Entity Framework in my projects. The speed is quite significant on simple queries. But I tried to map/reference a [1 to many- relation and read the documentation + examined the test code from the github page but without success. This is my example. Is there something I've forgot or should do to get it working like Entity Framework?
Example
// EF: returns +15.000 records + mapped > product.StockItems (slow)
dbContext.Products.Include(x => x.StockItems).ToList();
// OrmLite: returns +100.000 records (NO mapping > product.StockItems)
db.Select<Product>(db.From<Product>().Join<StockItem>());
// OrmLite: +15.000 separate requests to sql server (bad workarround + slow)
foreach (var product in db.Select<Product>())
{
// manual mapping
product.StockItems = db.Select<StockItem>(x => x.ProductId == product.Id);
}
Product.cs
public class Product
{
public int Id { get; set; }
public ProductType ProductType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int DisplayOrder { get; set; }
public bool LimitedToStores { get; set; }
public string Sku { get; set; }
public decimal Price { get; set; }
public decimal OldPrice { get; set; }
public decimal SpecialPrice { get; set; }
public decimal DiscountPercentage { get; set; }
public DateTime? DateChanged { get; set; }
public DateTime? DateCreated { get; set; }
//...
[Reference]
public virtual IList<StockItem> StockItems { get; set; } = new List<StockItem>();
}
StockItem.cs
public class StockItem
{
public int Id {get; set;}
[References(typeof(Product))]
public int ProductId { get; set; }
public string Size { get; set; }
public int TotalStockQuantity { get; set; }
public string Gtin { get; set; }
public int DisplayOrder { get; set; }
// ...
[Reference]
public virtual Product Product { get; set; }
}
Ideally your POCOs/DTOs shouldn't use interfaces and you don't need to use virtual as ORM only populates your own POCOs (i.e. it doesn't create proxies of your models like other Heavy ORMs), I also prefer to use [AutoIncrement] for integer Ids (unless you need to populate specific Ids) so my Models would look like:
public class Product
{
[AutoIncrement]
public int Id { get; set; }
public ProductType ProductType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int DisplayOrder { get; set; }
public bool LimitedToStores { get; set; }
public string Sku { get; set; }
public decimal Price { get; set; }
public decimal OldPrice { get; set; }
public decimal SpecialPrice { get; set; }
public decimal DiscountPercentage { get; set; }
public DateTime? DateChanged { get; set; }
public DateTime? DateCreated { get; set; }
[Reference]
public List<StockItem> StockItems { get; set; }
}
public class StockItem
{
[AutoIncrement]
public int Id { get; set; }
[References(typeof(Product))]
public int ProductId { get; set; }
public string Size { get; set; }
public int TotalStockQuantity { get; set; }
public string Gtin { get; set; }
public int DisplayOrder { get; set; }
}
OrmLite's POCO References only populate 1-level deep and it's not a good idea to have cyclical relationships as they're not serializable so I'd remove the back reference on StockItems as it's not going to be populated.
You also need to use LoadSelect in order to query and return POCOs with references, so to return Product with their StockItem references you can just do:
db.LoadSelect<Product>();
You can also populate this manually with 2 queries by using Merge extension method to merge 2 disconnected record sets, e.g:
var q = db.From<Product>().Join<StockItem>();
var products = db.Select(q.SelectDistinct());
var stockItems = db.Select<StockItem>();
products.Merge(stockItems);
Which will merge Products with their StockItems which you can quickly see by running:
products.PrintDump();
In my project I need to map objects from the external systems to DTOs. The object I want to map is:
public class PriceLists : List<PriceList> { }
I get the idea of mapping properties within the class but having difficulty finding a solution for this case. My DTO will preferably be "identical" to this source class to make it as simple as possible for the moment:
public class PriceListsDTO : List<PriceListDTO> { }
Is there a simple solution or do I need to refactor my DTO object?
Thanks.
Edit: I have tried creating mapping for a list of Price lists without success regarding this problem.
Mapper.Initialize(cfg => { cfg.CreateMap<PriceList>, <PriceListDTO>(); });
Mapper.Initialize(cfg => { cfg.CreateMap<IList<PriceList>, IList<PriceListDTO>>(); });
Edit2:
public class PriceList
{
public string Agreement { get; set; }
public Currency Currency { get; set; }
public string Description { get; set; }
public Nullable<DateTime> EndDate { get; set; }
public int Id { get; set; }
public Nullable<Guid> ImageKey { get; set; }
public bool IsBid { get; set; }
public bool IsLimitedToStock { get; set; }
public bool IsPrimary { get; set; }
public bool IsPublic { get; set; }
public string Name { get; set; }
public Nullable<DateTime> StartDate { get; set; }
public int Type { get; set; }
}
public class PriceListDTO
{
public string Agreement { get; set; }
public CurrencyViewModel Currency { get; set; }
public string Description { get; set; }
public DateTime? EndDate { get; set; }
public int Id { get; set; }
public Guid? ImageKey { get; set; }
public bool IsBid { get; set; }
public bool IsLimitedToStock { get; set; }
public bool IsPrimary { get; set; }
public bool IsPublic { get; set; }
public string Name { get; set; }
public DateTime? StartDate { get; set; }
public int Type { get; set; }
}
And the Currency class and DTO only contains string properties.
From the code you've given, you never actually told AutoMapper to associate the DTO with the model class. If you call Initialize twice, the second will REMOVE any previous mappings. Try updating your configuration to do the following:
Mapper.Initialize( cfg => {
cfg.CreateMap<PriceList, PriceListDTO>()
.ReverseMap();
// Not sure if this is required if you already have the model/dto map
cfg.CreateMap<IList<PriceList>, IList<PriceListDTO>>();
cfg.AssertConfigurationIsValid();
});
public class PriceList
{
public string Agreement { get; set; }
public Currency Currency { get; set; }
public string Description { get; set; }
public Nullable<DateTime> EndDate { get; set; }
public int Id { get; set; }
public Nullable<Guid> ImageKey { get; set; }
public bool IsBid { get; set; }
public bool IsLimitedToStock { get; set; }
public bool IsPrimary { get; set; }
public bool IsPublic { get; set; }
public string Name { get; set; }
public Nullable<DateTime> StartDate { get; set; }
public int Type { get; set; }
}
public class PriceListDTO
{
public string Agreement { get; set; }
public Currency Currency { get; set; }
public string Description { get; set; }
public DateTime? EndDate { get; set; }
public int Id { get; set; }
public Guid? ImageKey { get; set; }
public bool IsBid { get; set; }
public bool IsLimitedToStock { get; set; }
public bool IsPrimary { get; set; }
public bool IsPublic { get; set; }
public string Name { get; set; }
public DateTime? StartDate { get; set; }
public int Type { get; set; }
}
after that try automapper.mapper.createmap it
will work for you otherwise you need to use formember method to map
properties of currency with currencyviewmodel one by one because
object are different to each other just try with it. hope it will help
for you . Thanks
I have a statement in one of my entities which uses a foreign key to return an IEnumerable<CustomField>.
I have used LINQ in my repository to test the below method to see if it works and it does. But when I use the foreign key reference in the entity it returns null. Am I missing something here? How can I use a foreign key to gain access to the data in another entity.
Invoice entity:
[Table("vwinvoice")]
public class Invoice
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Sys_InvoiceID { get; set; }
[DisplayName("Inc.In Turnover")]
public bool Turnover { get; set; }
public int FK_StatusID { get; set; }
[DisplayName("Invoice No.")]
public string InvoiceNumber { get; set; }
[DisplayName("Invoice Date")]
public DateTime InvoiceDate { get; set; }
[DisplayName("Document Type")]
public string DocType { get; set; }
[DisplayName("Supplier Invoice No.")]
[Column("SupplierInvoiceNumber")]
public string SuppInvNumber { get; set; }
public int FK_SupplierID { get; set; }
[DisplayName("Account Number")]
public string AccountNumber { get; set; }
[DisplayName("Order Number")]
public string OrderNumber { get; set; }
[DisplayName("Order Date")]
public DateTime? OrderDate { get; set; }
[DisplayName("Currency Code_Doc")]
public string CurrencyCode_Doc { get; set; }
[DisplayName("Net Amount_Doc")]
public decimal? NetAmount_Doc { get; set; }
[DisplayName("VAT Amount_Doc")]
public decimal? VATAmount_Doc { get; set; }
[DisplayName("Gross Amount_Doc")]
[Required]
public decimal? GrossAmount_Doc { get; set; }
[DisplayName("Currency Code_Home")]
public string CurrencyCode_Home { get; set; }
[DisplayName("Net Amount_Home")]
public decimal? NetAmount_Home { get; set; }
[DisplayName("VAT Amount_Home")]
public decimal? VATAmount_Home { get; set; }
[DisplayName("Gross Amount_Home")]
public decimal? GrossAmount_Home { get; set; }
[DisplayName("Payment Reference")]
public string PaymentReference { get; set; }
[DisplayName("Supplier")]
public string AccountName { get; set; }
[DisplayName("Status")]
public string StatusName { get; set; }
[DisplayName("Auditor Comments")]
public string AuditorComments { get; set; }
[DisplayName("Reviewer Comments")]
public string ReviewerComments { get; set; }
[DisplayName("Data Source")]
[Required]
public string DataOrigin { get; set; }
public int DetailLineCount { get; set; }
public IEnumerable<CustomField> ClientData {
get {
//Use the CustomFields foreign key to gain access to the data returns null.
return GetCustomFieldData(this.CustomFields.Select(r => r));
}
}
private IEnumerable<CustomField> GetCustomFieldData(IEnumerable<Entities.CustomFields> enumerable) {
return (from f in enumerable
select new CustomField {
Name = f.FK_CustomHeader,
Value = f.Value
});
}
//Custom Field Additions
public virtual ICollection<CustomFields> CustomFields { get; set; }
}
CustomFields entity:
[Table("tblCustomFields")]
public class CustomFields
{
[Key]
public int ID { get; set; }
public int? FK_SysInvoiceID { get; set; }
[StringLength(255)]
public string FK_CustomHeader { get; set; }
[StringLength(255)]
public string Value { get; set; }
public virtual Invoice Invoices { get; set; }
public virtual CustomFieldHeaders CustomFieldHeaders { get; set; }
}
I also cannot place a breakpoint in the get statement to see what happens, why is this? It just skips over the breakpoint whenever I try to return a list of Invoices, which can be seen here:
public IQueryable<Invoice> Invoices
{
get
{
var x = _ctx.Invoices.ToList();
return _ctx.Invoices;
}
}
You are using the virtual keyword when declaring your CustomFields property. As such it will be lazy loaded. If you want the property to be populated once returned from the repository you will need to explicitly Include the table in your method:
var x = _ctx.Invoices.Include(i => i.CustomFields).ToList();
return _ctx.Invoices;
Or you can remove the virtual keyword and the property will always be populated, with the consequent performance hit of the database join and the extra data being returned whenever you access Invoices.