Dotnet core automapper orderItems - asp.net-core

Now I am creating a website where you can place orders.
There we have Order, OrderItem and Item.
public class Order
{
public int Id { get; set; }
public User User { get; set; }
public int? UserId { get; set; }
public ICollection<OrderItem> OrderItems { get; set; }
public int TotalPrice { get; set; }
public string Status { get; set; }
public DateTime CreatedAt { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int ItemId { get; set; }
public virtual Item Item { get; set; }
public int Count { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
public double TotalPrice { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int ItemId { get; set; }
public Item Item { get; set; }
public int Count { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
public double TotalPrice { get; set; }
}
Upon creation of the order I will receive:
{
"orderItems": [
{
"itemId": 1,
"count": 4
}
]
}
and I have added in controller:
var userFromRepo = await _repo.GetUser (userId);
OrderForCreationDto.UserId = userId;
var order = _mapper.Map<Order> (OrderForCreationDto);
// userFromRepo.Orders.Add(order);
_repo.Add (order);
if (await _repo.SaveAll ())
{
var OrderToReturn = _mapper.Map<OrderForCreationDto> (order);
return Ok (OrderToReturn);
}
and in automapper:
CreateMap<Order, OrderForCreationDto> ();
CreateMap<OrderItemForCreationDto, OrderItem> ()
.ForMember (dest => dest.TotalPrice, opt => {
opt.MapFrom (d => d.Item);
opt.ResolveUsing (d => d.Item.Price * d.Count);
});
but always the mapping for OrderItem => item is null and so its giving error inside automapper.
I know its a long question but its killing me, I would appreciate if you could help. Thank you in advance

You are using older version of Automapper. In the version 8.0 ResolveUsing was consolidated with MapFrom.
Looks like you have source and destination switched. Try code below or adding .Reversemap()
CreateMap<OrderItem, OrderItemForCreationDto> ()
.ForMember (dest => dest.TotalPrice, opt =>
{
opt.MapFrom (d => d.Item);
opt.ResolveUsing (d => d.Item.Price * d.Count);
});

Related

Entity Framework Core Fluent Api config join table record depend on value

I have a join table UserSystem which is:
public class UserSystem
{
public int UserSystemId { get; set; }
public int SystemId { get; set; }
public virtual System System { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public DateTime AddedDate { get; set; }
public int AddedByUserId { get; set; }
public bool Deleted { get; set; }
public DateTime? DeletedDate { get; set; }
public int? DeletedByUserId { get; set; }
}
And a configuration
public void Configure(EntityTypeBuilder<UserSystem> builder)
{
builder.ToTable("UserSystem", DbSchemas.Dbo);
builder.HasKey(e => e.UserSystemId);
builder.HasOne(bc => bc.User).WithMany(b => b.UserSystems).HasForeignKey(bc => bc.UserId);
builder.HasOne(bc => bc.System).WithMany().HasForeignKey(bc => bc.SystemId);
}
Now I want to be able to add new UserSystem with same UserId and SystemId, but only when same data not exists with Deleted == 0. If for example UserId = 1 and SystemId = 1 are in database, but it has Deleted = 1 then I want to be able to add same record again with Deleted = 0, but not being able to add same data when any record with that data has Deleted = 0. How can I achieve it in FluentApi?

Querying DTOs based on EF using Odata

I have an ASP.NET Core Web API setup with a SQL Server database and an EF data model.
Versions:
EF: Microsoft.EntityFrameworkCore 5.0.0-preview.7.20365.15
OData: Microsoft.AspNetCore.OData 7.4.1
.Net Core 3.1
The question is: I can't use filter, select in expand (nested expand). Example URLs which OData does not add filters to the where condition of SQL query which is seen on SQL Server Profiler:
https://localhost:44327/odata/clientcontract?$expand=ContactsInfo($filter=value eq '100003265')
https://localhost:44327/odata/clientcontract?$expand=Documents($filter=documentnnumber eq '100003265')
These are my database-first entity models:
public partial class ClientRef
{
public ClientRef()
{
Addresses = new HashSet<Address>();
Assets = new HashSet<Asset>();
ClientContactInfoComps = new HashSet<ClientContactInfoComp>();
ClientRelationCompClient1Navigations = new HashSet<ClientRelationComp>();
ClientRelationCompClient2Navigations = new HashSet<ClientRelationComp>();
Clients = new HashSet<Client>();
CommentComps = new HashSet<CommentComp>();
Companies = new HashSet<Company>();
Documents = new HashSet<Document>();
PhysicalPeople = new HashSet<PhysicalPerson>();
}
[Column("Inn")]
public int Id { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Asset> Assets { get; set; }
public virtual ICollection<ClientContactInfoComp> ClientContactInfoComps { get; set; }
public virtual ICollection<ClientRelationComp> ClientRelationCompClient1Navigations { get; set; }
public virtual ICollection<ClientRelationComp> ClientRelationCompClient2Navigations { get; set; }
public virtual ICollection<Client> Clients { get; set; }
public virtual ICollection<CommentComp> CommentComps { get; set; }
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Document> Documents { get; set; }
public virtual ICollection<PhysicalPerson> PhysicalPeople { get; set; }
}
public partial class Document
{
public int Id { get; set; }
public string DocumentNumber { get; set; }
public int DocumentType { get; set; }
public int Inn { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
public DateTime? DocumentExpireDate { get; set; }
public virtual ClientRef InnNavigation { get; set; }
}
public partial class ClientContactInfoComp
{
public int Id { get; set; }
public int Inn { get; set; }
public int ContactInfoId { get; set; }
public int? Point { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
[ForeignKey("ContactInfoId")]
public ContactInfo ContactInfo { get; set; }
public virtual ClientRef InnNavigation { get; set; }
}
public partial class ContactInfo
{
public ContactInfo()
{
CallHistories = new HashSet<CallHistory>();
ClientContactInfoComps = new HashSet<ClientContactInfoComp>();
CommentComps = new HashSet<CommentComp>();
}
public int Id { get; set; }
public int? Type { get; set; }
public string Value { get; set; }
public virtual ICollection<CallHistory> CallHistories { get; set; }
public virtual ICollection<ClientContactInfoComp> ClientContactInfoComps { get; set; }
public virtual ICollection<CommentComp> CommentComps { get; set; }
}
public partial class CommentComp
{
public int Id { get; set; }
public int Inn { get; set; }
public int? CommentId { get; set; }
public int? ContactId { get; set; }
public virtual Comment Comment { get; set; }
public virtual ContactInfo Contact { get; set; }
public virtual ClientRef InnNavigation { get; set; }
}
public partial class Comment
{
public Comment()
{
CommentComps = new HashSet<CommentComp>();
}
public int Id { get; set; }
public string Text { get; set; }
public DateTime? CreateTimestamp { get; set; }
public string Creator { get; set; }
public virtual ICollection<CommentComp> CommentComps { get; set; }
}
These are my DTOs:
[DataContract]
public class ClientContract
{
public ClientContract()
{
ContactsInfo = new List<ContactInfoContract>();
Documents = new List<DocumentContract>();
Relations = new List<RelationContract>();
ClientComment = new CommentContract();
}
[DataMember(Name = "INN")]
[Key]
public int INN { get; set; }
[DataMember(Name = "validfrom")]
public DateTime ValidFrom { get; set; }
[DataMember(Name = "validto")]
public DateTime ValidTo { get; set; }
[DataMember(Name = "clienttype")]
public ClientType ClientType { get; set; }
[DataMember(Name = "companyname")]
public string CompanyName { get; set; }
[DataMember(Name = "firstname")]
public string FirstName { get; set; }
[DataMember(Name = "lastname")]
public string LastName { get; set; }
[DataMember(Name = "fathername")]
public string FatherName { get; set; }
[DataMember(Name = "pinnumber")]
public string PinNumber { get; set; }
[DataMember(Name = "birthdate")]
public DateTime? BirthDate { get; set; }
[DataMember(Name = "positioncustom")]
public string PositionCustom { get; set; }
[DataMember(Name = "position")]
public int Position { get; set; }
[DataMember(Name = "monthlyincome")]
public decimal MonthlyIncome { get; set; }
[DataMember(Name = "clientcomment")]
public CommentContract ClientComment { get; set; }
[DataMember(Name = "contactsinfo")]
public List<ContactInfoContract> ContactsInfo { get; set; }
[DataMember(Name = "documents")]
[ForeignKey("Documents")]
public List<DocumentContract> Documents { get; set; }
[DataMember(Name = "relations")]
public List<RelationContract> Relations { get; set; }
}
public class DocumentContract
{
[Key]
public int Id { get; set; }
[DataMember(Name = "documentNumber")]
public string documentNumber { get; set; }
[DataMember(Name = "documentType")]
public int documentType { get; set; }
[DataMember(Name = "documentexpiredate")]
public DateTime? documentExpireDate { get; set; }
}
[DataContract]
public class ContactInfoContract
{
public ContactInfoContract()
{
ContactComment = new CommentContract();
}
[Key]
public int Id { get; set; }
[DataMember(Name = "type")]
public int Type { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
[DataMember(Name = "contactComment")]
public CommentContract ContactComment { get; set; }
}
public class CommentContract
{
[DataMember(Name = "Text")]
public string Text { get; set; }
[DataMember(Name = "creator")]
public string Creator { get; set; }
}
In DTOs there is not relation model. For instance: in EF, there is a ClientContactInfoComp model, which connects ClientRefs and ContactInfo models, but in DTO ClientContract is directly referenced with ContactInfoContract.
Model Builder in Startup.cs
private IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<ClientContract>("ClientContract").EntityType.HasKey(x => x.INN).Name = "ClientRef";
builder.EntitySet<DocumentContract>("DocumentContract");
builder.EntitySet<ContactInfoContract>("ContactInfoContracts").EntityType.HasKey(x => x.Id);
return builder.GetEdmModel();
}
public class ClientContractController : ControllerBase
{
[EnableQuery(MaxExpansionDepth = 10)]
public IQueryable<ClientContract> Get()
{
var clientRefs = _context.ClientRefs
.Include(x => x.Clients.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.PhysicalPeople.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.Companies.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.Documents.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.ClientContactInfoComps.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.ThenInclude(x => x.ContactInfo)
.Include(x => x.Assets.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now));
List<ClientContract> contracts = new List<ClientContract>();
foreach (var clientRef in clientRefs)
{
ClientContract clientContract = new ClientContract() { INN = clientRef.Id };
foreach(var c in clientRef.Clients)
{
clientContract.ClientType = (ClientType) c.ClientType;
}
foreach (var pp in clientRef.PhysicalPeople)
{
clientContract.FirstName = pp.FirstName;
clientContract.LastName = pp.LastName;
}
foreach (var comp in clientRef.Companies)
{
clientContract.CompanyName = comp.CompanyName;
}
foreach (var doc in clientRef.Documents)
{
clientContract.Documents.Add(new DocumentContract()
{
documentNumber = doc.DocumentNumber,
documentExpireDate = doc.DocumentExpireDate,
documentType = doc.DocumentType
});
}
foreach (var comp in clientRef.ClientContactInfoComps)
{
clientContract.ContactsInfo.Add(new ContactInfoContract
{
Type = comp.ContactInfo.Type.Value,
Value = comp.ContactInfo.Value
});
}
contracts.Add(clientContract);
}
return contracts.AsQueryable();
}
}
Yes I getting result from following links:
https://localhost:44327/odata/clientcontract
https://localhost:44327/odata/clientcontract?$filter=Id eq 4
https://localhost:44327/odata/clientcontract?$expand=documents,contactsinfo
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();//.AddNewtonsoftJson(); ;
services.AddDbContext<DbContext>(options =>
options.UseSqlServer("connectionstring"));
services.AddOData();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
//endpoints.EnableDependencyInjection();
endpoints.Expand().Select().Filter().OrderBy().Count().MaxTop(10);
endpoints.MapODataRoute("odata", "odata", GetEdmModel());
});
}
private IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<ClientContract>("ClientContract").EntityType.HasKey(x => x.INN).Name = "ClientRef";
builder.EntitySet<DocumentContract>("DocumentContract");
builder.EntitySet<ContactInfoContract>("ContactInfoContracts").EntityType.HasKey(x => x.Id);
return builder.GetEdmModel();
}
}
You should use the property name and not the attribute Name when use OData.
OData client library relies on it's own attribute OriginalNameAttribute to gain knowledge about class/member names as server emits them. The details you can see from here.
It works when I remove [DataContract] attribute.
public class ClientContract
{
public ClientContract()
{
ContactsInfo = new List<ContactInfoContract>();
}
[Key]
public int INN { get; set; }
public string CompanyName { get; set; }
public List<ContactInfoContract> ContactsInfo { get; set; }
}
public class ContactInfoContract
{
[Key]
public int Id { get; set; }
[DataMember(Name = "type")]
public int Type { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
}

Entity Framework Parent\Child Retrieval

I have an ID of a "Component" parent record that I need to retrieve all the "Attachment" child records. The grid I am using need fields returned an placed in "TheComponentAttachmentsJoinedTable" I am only able to retrieve one record. I have used the value of FirstorDefault. That is the only way it will accept the line of code without complaining about the IEnumerable to int. Could someone please explain what is incorrect?
public class Component
{
public int ID { get; set; }
public string ComponentCode { get; set; }
public string ComponentDescription { get; set; }
public double ComponentWeight { get; set; }
public double ComponentQuantity { get; set; }
public string LastModUser { get; set; }
public DateTime LastModDate { get; set; }
public virtual ICollection<Attachment> Attachments { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Element> Elements { get; set; }
}
public class Attachment
{
public int ID { get; set; }
public string AttachmentDescription { get; set; }
public string OriginalName { get; set; }
public string MimeType { get; set; }
public byte[] bytes { get; set; }
public string LastModUser { get; set; }
public DateTime LastModDate { get; set; }
//Navigation
public virtual ICollection<Element> Elements { get; set; }
public virtual ICollection<Component> Components { get; set; } //lt M-m
}
public class TheComponentAttachmentsJoinedTable
{
public int ComponentID { get; set; }
public int AttachmentID { get; set; }
public string OriginalName { get; set; }
}
public static List<TheComponentAttachmentsJoinedTable> ComponentAttachments_GetAllByComponentID(int ComponentID)
{
using (TheContext TheDB = new TheContext())
{
var r = (from x in TheDB.Component
.Where(x => x.ID == ComponentID)
.Include(x => x.Attachments)
select new TheComponentAttachmentsJoinedTable
{
ComponentID = x.ID,
AttachmentID = x.Attachments.Select(y => (y.ID)).FirstOrDefault(),
OriginalName = x.Attachments.Select(y => y.OriginalName).FirstOrDefault().ToString(),
}
);
return r.ToList();
}

How to join when using groupby

I have two tables of picturedetails and picture likes in mydatabase.
public partial class picturedetail
{
public int idpictures { get; set; }
public int iduser { get; set; }
public string picTitle { get; set; }
public string picFilename { get; set; }
public System.DateTime pictime { get; set; }
public int likes { get; set; }
public int nuditylevel { get; set; }
public int fakeslevel { get; set; }
}
public partial class picturelike
{
public int idpicturelike { get; set; }
public int idpictures { get; set; }
public int iduser { get; set; }
public System.DateTime iddatetime { get; set; }
public int iduserlikedby { get; set; }
public int likenumber { get; set; }
}
Its my Data Transfer Object (DTO) to avoid anonymous type error
public class LikesandPictureDetails
{
public int IdPicturess { get; set; }
public int IdUserPic { get; set; }
public string PicTitle { get; set; }
public string picFilename { get; set; }
public System.DateTime? PicTime { get; set; }
public int Likes { get; set; }
public int NudityLevel { get; set; }
public int FakesLevel { get; set; }
public int IdPicturelike { get; set; }
public int IdUser { get; set; }
public System.DateTime? IdDatetime { get; set; }
public int IduserLikedby { get; set; }
public int LikeNumber { get; set; }
public int IdPictures { get; set; }
public float totalrating { get; set; }
public int Count { get; set; }
}
I am successfully able to count the individual ratings of the picture and then divide by their counts. Now i want to show totalrating in my picture details table. How to achieve this thing? This is my code:
var query =
db.picturelikes.GroupBy(n => n.idpictures).Select(group =>
new LikesandPictureDetails
{
IdPictures = group.Key,
totalrating = (group.AsEnumerable().Sum(o => o.individualrating)) / group.Count(),
});
here likesandpicturedetails is acting as a dto.
Well, first of all you should use method Average to calculate average value. But in your case
But, as you need to calculate overall rating, why don't you calculate sum and average in the same step:
db.picturelikes.GroupBy(n => n.idpictures).Select(group =>
new LikesandPictureDetails
{
IdPictures = group.Key,
averageRating = (group.AsEnumerable().Average(o => o.individualrating)),
totalRating = (group.AsEnumerable().Sum(o => o.individualrating)),
});

How do I construct my RavenDb where clause for this document, given these requirements?

This question builds on the following question (s)
Indexing : How do I construct my RavenDb static indexes for this document, given these requirements?
Simple Where clause with paging : How to construct a proper WHERE clause with RavenDb
The essence of the question is how do I dynamically add or remove fields to participate in a where clause?
Document:
[Serializable]
public class Product
{
public string AveWeight { get; set; }
public string BrandName { get; set; }
public string CasePack { get; set; }
public string Catalog { get; set; }
public decimal CatalogId { get; set; }
public decimal CategoryId { get; set; }
public string Info { get; set; }
public bool IsOfflineSupplierItem { get; set; }
public bool IsRebateItem { get; set; }
public bool IsSpecialOrderItem { get; set; }
public bool IsSpecialPriceItem { get; set; }
public bool IsTieredPricingItem { get; set; }
public string ItemNum { get; set; }
public string ManufactureName { get; set; }
public string ManufactureNum { get; set; }
public decimal OffineSupplierId { get; set; }
public string PackageRemarks { get; set; }
public decimal Price { get; set; }
public decimal PriceGroupId { get; set; }
public decimal ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public string SupplierName { get; set; }
public string UOM { get; set; }
public string Upc { get; set; }
public string Url { get; set; }
}
Index:
if (store.DatabaseCommands.GetIndex("Products_Index") == null)
{
store.DatabaseCommands.PutIndex("Products_Index", new IndexDefinitionBuilder<Product>
{
Map = products => from p in products
select new { p.CatalogId,
p.HasPicture,
p.INFO2,
p.IsOfflineSupplierItem,
p.IsRebateItem,
p.IsSpecialOrderItem,
p.IsSpecialPriceItem,
p.IsTieredPricingItem,
p.Price },
Indexes =
{
{ x => x.INFO2, FieldIndexing.Analyzed },
{ x => x.CatalogId, FieldIndexing.Default},
{ x => x.HasPicture, FieldIndexing.Default},
{ x => x.IsOfflineSupplierItem, FieldIndexing.Default},
{ x => x.IsRebateItem, FieldIndexing.Default},
{ x => x.IsSpecialOrderItem, FieldIndexing.Default},
{ x => x.IsSpecialPriceItem, FieldIndexing.Default},
{ x => x.IsTieredPricingItem, FieldIndexing.Default},
{ x => x.Price, FieldIndexing.Default}
}
});
}
Naive Where clause
string t1 = "foo";
bool t2 = true;
decimal t3 = 100m;
products = DocumentSession.Query<Product>()
.Statistics(out stats)
.Where(p => p.INFO2.StartsWith(t1) && p.IsRebateItem == t2 && p.CatalogId = t3)
.OrderByField(columnToSortBy, columnToSortByAsc)
.Skip(pageIndex * pageSize)
.Take(pageSize)
.ToList()
;
First Pass at Advanced Query
var products = s.Advanced.LuceneQuery<Product>("Products")
.WhereEquals("Catalog", "National Catalog")
.ToList()
;
which throws an exception
A first chance exception of type 'Lucene.Net.QueryParsers.QueryParser.LookaheadSuccess' occurred in Lucene.Net.dll
A first chance exception of type 'System.IO.IOException' occurred in Lucene.Net.dll
Second pass (works)
result = s.Advanced.LuceneQuery<Product>("Products_Index")
.Where("CatalogId:(736275001) AND HasPicture:(true) AND IsOfflineSupplierItem:(false)")
.ToArray();
Third Pass (and fastest yet)
result = s.Advanced.LuceneQuery<Product>("Products/Index")
.Statistics(out stats)
.WhereStartsWith("INFO2", "ink")
.AndAlso()
.WhereStartsWith("INFO2", "pen")
.AndAlso()
.WhereEquals("CatalogId", 736275001)
.AndAlso()
.WhereEquals("HasPicture", true)
.AndAlso()
.WhereEquals("IsOfflineSupplierItem", false)
.AndAlso()
.WhereEquals("IsRebateItem", false)
.AndAlso()
.WhereEquals("IsSpecialOrderItem", false)
.AndAlso()
.WhereEquals("IsSpecialPriceItem", false)
.ToArray()
;
If you want to do this dynamically, you can use the DocumentSession.Advanced.LuceneQuery, which allows you to pass strings as the property names for the index.
That way, you don't have to deal with the strongly typed issues.