I'm getting data from an Oracle database and creating an API for internal use. Some of the classes are (shortened for brevity):
CustomerOrderTab
Public partial class CustomerOrderTab
Public string OrderNo { get; set; }
[ForeignKey("CustomerMV")]
public string CustomerNo { get; set; }
public List<CustomerOrderLineTab> CustomerOrderLines { get; set; }
public List<CustomerOrderChargeTab> CustomerOrderCharge { get; set; }
public List<ShipmentOrderLineTab> ShipmentOrderLines { get; set; }
public CustomerMV CustomerMV { get; set; }
CustomerOrderDTO
Public class OrderDTO
public string OrderNo { get; set; }
public string CustomerName { get; set; }
public List<OrderLinesDTO> OrderLines { get; set; }
public List<OrderChargesDTO> OrderCharges { get; set; }
public List<ShipmentOrderLinesDTO> ShipmentOrderLines { get; set; }
ShipmentOrderLineTab
public partial class ShipmentOrderLineTab
public decimal ShipmentId { get; set; }
[ForeignKey("CustomerOrderTab")]
public string OrderNo { get; set; }
public ShipmentTab Shipment { get; set; }
ShipmentOrderLineDTO
public class ShipmentOrderLinesDTO
public decimal ShipmentId { get; set; }
public string OrderNo { get; set; }
public ShipmentTab MyShipment { get; set; }
ShipmentTab
public partial class ShipmentTab
[ForeignKey("ShipmentOrderLineTab")]
public decimal ShipmentId { get; set; }
public string ShipperAddress1 { get; set; }
public string ShipperAddress2 { get; set; }
public List<ShipmentHandlingUnitTab> ShipmentHandlingUnits { get; set; }
ShipmentDTO
public class ShipmentDTO
public decimal ShipmentId { get; set; }
public string ShipViaCode { get; set; }
public string ShipmentPayer { get; set; }
public List<ShipmentHandlingUnitDTO> ShipmentHandlingUnitDTOs { get; set; }
This Works:
var orderLines = from o in _context.CustomerOrderTab
.Where(o => o.OrderNo == orderno)
select new OrderDTO()
{
OrderNo = o.OrderNo,
CustomerName = o.CustomerMV.CustomerName,
This is a child of the order ShipmentOrderLines = o.ShipmentOrderLines
This works .Select(ob => new ShipmentOrderLinesDTO
{
ShipmentId = ob.ShipmentId,
OrderNo = ob.OrderNo,
MyShipment = ob.Shipment
Adding this does not work .Select(y => new ShipmentDTO
The error says that ShipmentTab
Doesn’t have a definition for Select {
}
}).ToList()
Shipment is a child of ShipmentOrderLine (there will only be one shipment per line)
ShipmentOrderLine is a child of CustomerOrder (there can be many lines per order)
I think the problem is that there is only one shipment per line but I've tried lots of things and can't get it to map to my DTO.
Fix your model:
ShipmentOrderLineDTO
public class ShipmentOrderLinesDTO
public decimal ShipmentId { get; set; }
public string OrderNo { get; set; }
public ShipmentDTO MyShipment { get; set; }
Select is only used to project a collection. For non-collections, you just create a new object...
var orderLines = _context.CustomerOrderTab
.Where(o => o.OrderNo == orderno)
.Select(o => new OrderDTO {
OrderNo = o.OrderNo,
CustomerName = o.CustomerMV.CustomerName,
ShipmentOrderLines = o.ShipmentOrderLines
.Select(ob => new ShipmentOrderLinesDTO {
ShipmentId = ob.ShipmentId,
OrderNo = ob.OrderNo,
MyShipment = new ShipmentDTO {
ShipmentId = ob.Shipment.ShipmentId,
...
ShipmentHandlingUnits = ob.Shipment.ShipmentHandlingUnits
.Select(shu=> new ShipmentHandlingUnitDTO {
...
}).ToList()
}
}
}).ToList()
Now with that all said, I typically have mapping extension methods on my data model classes, which allow things like this:
var orderLines = _context.CustomerOrderTab
.Where(o=>o.OrderNo == orderno)
.Select(o=> o.ToDto());
Example class:
public static class CustomerOrderTabExtensions {
public static CustomerOrderDto ToDto(this CustomerOrderTab cot) {
return new CustomerOrderDto {
OrderNo = cot.OrderNo,
...
OrderLines = cot.CustomerOrderLines
.Select(col=>col.ToDto())
.ToList()
}
}
}
Or use Automapper.
Related
I have got a working code to join two mongo collections using $lookup.
[HttpGet("mongojoin")]
public IActionResult GetMongoJoinCollection([FromQuery(Name = "schoolId")] Guid schoolId)
{
var tabAssessmentCollection = mongoDatabase.GetCollection<TabAssessmentDocument>(MongoSettings.TabAssessmentDocumentName);
var tabComponentCollection =
mongoDatabase.GetCollection<TabComponentDocument>(MongoSettings.TabComponentDocumentName);
var resultMongo = tabAssessmentCollection
.Aggregate()
.Match(d => d.SchoolId.Equals(schoolId))
.Lookup(
foreignCollection: tabComponentCollection,
localField: ta => ta.ComponentId,
foreignField: tc => tc.TblId,
#as: (TabAssessmentDocument ta) => ta.Component
)
.Unwind(d => d.Component)
.ToList();
var resultList = new List<TabSubjectDocumentViewModel>();
foreach (var row in resultMongo)
{
resultList.Add(new TabSubjectDocumentViewModel()
{
Id = row["TblId"].AsGuid,
SchoolId = row["SchoolId"].AsGuid,
Name = row["Name"].AsString,
ShortName = row["ShortName"].AsString,
Active = row["Active"].AsNullableBoolean,
TabComponent = row["Component"]["Name"].AsString
});
}
return Ok(resultList);
}
}
public class TabSubjectDocumentViewModel
{
public Guid Id { get; set; }
public Guid? SchoolId { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public bool? Active { get; set; }
public string TabComponent { get; set; }
}
public class TabAssessmentDocument
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public Guid TblId { get; set; }
public Guid? SchoolId { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public Guid? ComponentId { get; set; }
public bool? Active { get; set; }
}
public class TabComponentDocument
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public Guid TblId { get; set; }
public Guid? SchoolId { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public string ComponentType { get; set; }
public bool? Active { get; set; }
}
I tried using the Project method just after the .Unwind(d => d.Component), but, then I was not getting the $lookup collection data (TabComponent) in the Bsondocument. If I could Serialize this to a List, how should I be doing it ? Can anyone tell me how I should using the Projection and Serialization here ?
I have strange problem. I have to pass the following Xunit test:
Fact]
public void GetOrder_ValidId_ReturnsCompleteOrder()
{
var service = new DataService();
var order = service.GetOrder(10248);
Assert.Equal(3, order.OrderDetails.Count);
Assert.Equal("Queso Cabrales", order.OrderDetails.First().Product.Name);
Assert.Equal("Dairy Products", order.OrderDetails.First().Product.Category.Name);
}
I am able to pass the orderdetails list to the test. However the "orderdetail" objects that i pass have a product object with null.
My classes are:
public class Order
{
[Key] public int Id { get; set; }
public DateTime Date { get; set; }
public DateTime Required { get; set; }
[Required] public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public string ShipName { get; set; }
public string ShipCity { get; set; }
public Order()
{
this.OrderDetails = new List<OrderDetail>();
}
public override string ToString()
{
string ret =
$"Id = {Id}, DateTime = {Date}, Required = {Required}, shipName= {ShipName}, Shipcity = {ShipCity}";
return ret;
}
}
public class OrderDetail
{
public Order Order { get; set; }
[ForeignKey("orders")]
public int Orderid { get; set; }
[ForeignKey("products")]
public int productid { get; set; }
public int UnitPrice { get; set; }
public int Quantity { get; set; }
public int Discount { get; set; }
[Required] public virtual Product Product { get; set; }
public OrderDetail()
{
Product = Product;
}
public override string ToString()
{
return
$"OrderId = {Orderid}, Productid = {productid}";
}
}
public class Product
{
[ForeignKey("orderdetails")] public int Id { get; set; }
public string Name { get; set; }
public float UnitPrice { get; set; }
public string QuantityPerUnit { get; set; }
public int UnitsInStock { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public override string ToString()
{
return
$"Id = {Id}, Name = {Name}, UnitPrice = {UnitPrice}, QuantityPerUnit = {QuantityPerUnit}, UnitsInStock = {UnitsInStock}, CategoryId = {CategoryId}";
}
}
I have the tried the following two solution:
public Order GetOrder(int id)
{
using var ctx = new NorthWindContext();
var query = ctx.Orders.AsQueryable().Where(o => o.Id == 10248).FirstOrDefault();
ctx.SaveChanges();
//var query2 = ctx.Orders.Include("orderdetails").Where()
return query;
}
and
var query2 = ctx.Orders.Where(o => o.Id == 10248)
.Select(a => new Order
{
Id = a.Id,
OrderDetails = a.OrderDetails
}).FirstOrDefault();
I have tried to reconfigure the mapping but didn't do it.
If i do the same query and use Console.Writeline in a foreach loop i can conclude that every orderdetail has a "product"...
I keep on the getting the error: "Object not set to an instance of an object" when passing to xUnit test.
Ivans response did the job.
First of all i removed using System.Data.Entity;
Then i changed the order class to the following:
public class Order
{
[Key] public int Id { get; set; }
public DateTime Date { get; set; }
public DateTime Required { get; set; }
[Required] public virtual List<OrderDetail> OrderDetails { get; set; }
public string ShipName { get; set; }
public string ShipCity { get; set; }
public Order()
{
this.OrderDetails = new List<OrderDetail>();
}
public override string ToString()
{
string ret =
$"Id = {Id}, DateTime = {Date}, Required = {Required}, shipName= {ShipName}, Shipcity = {ShipCity}";
return ret;
}
}
Then i used the following query just like Ivans:
var query3 = ctx.Orders
.Include(o => o.OrderDetails)
.ThenInclude(d => d.Product)
.ThenInclude(d => d.Category)
.AsSingleQuery()
.FirstOrDefault(o => o.Id == 10248);
I hav been using automapper for sometime trying figure out how to handle different situation. I came across below situation and need some help figuring out the best approach. Below are my EF related classes;
public sealed class Invoice
{
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public Transaction InvoiceTransaction { get; set; }
public int TransactionID { get; set; }
}
public sealed class Transaction
{
public Transaction()
{
this.TransactionItems = new List<TransactionDetail>();
}
public int TransactionID { get; set; }
public DateTime TransactionDate { get; set; }
public DateTime TransactionLogDate { get; set; }
public TransactionType TransactionType { get; set; }
public IList<TransactionDetail> TransactionItems { get; set; }
public Invoice RefferingInvoice { get; set; }
public string Remarks { get; set; }
}
public sealed class TransactionDetail
{
public int TransactionID { get; set; }
public string ProductItemcode { get; set; }
public Product Product { get; set; }
public double Qty
{
get
{
return Math.Abs(this.StockChangeQty);
}
}
public double StockChangeQty { get; set; }
public double? UnitPrice { get; set; }
}
public sealed class Product
{
public Product()
{
this.StockTransactions = new List<TransactionDetail>();
}
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double UnitPrice { get; set; }
public IList<TransactionDetail> StockTransactions { get; set; }
public double Qty
{
get
{
if (this.StockTransactions.Count == 0)
{
return 0;
}
else
{
return this.StockTransactions.Sum(x => x.StockChangeQty);
}
}
}
public bool Discontinued { get; set; }
}
These are my view model classes;
public class InvoiceReportViewModel
{
public InvoiceReportViewModel()
{
LineItems = new List<InvoiceReportLineItemViewModel>();
}
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public string Remarks { get; set; }
public string StringInvoiceNo
{
get
{
return InvoiceID.ToString("########");
}
}
public IList<InvoiceReportLineItemViewModel> LineItems { get; set; }
}
public class InvoiceReportLineItemViewModel
{
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double? UnitPrice { get; set; }
public double Qty { get; set; }
public double LineTotal
{
get
{
if (UnitPrice.HasValue)
{
return UnitPrice.Value * Qty;
}
else
{
return 0;
}
}
}
}
My requirement is to convert the Invoice EF object to InvoiceReportViewModel object.
To do this I need to setup the profile. This is where I run into a problem; as it's not straight forward. The only way I see this done is by create my own Resolver by extending TypeConverter and manually doing the conversion by overriding ConvertCore method.
If there another way of getting this done (something with less work)???
Also I feel I could Map TransactionDetails EF class to InvoiceReportLineItemViewModel class by using the Mapper.CreateMap()..ForMember(...
But how can I use the mapper to convert it within the ConvertCore method?
Thanks in advance
In your case I do not see any requirements to use any custom converters.
You can convert Invoice EF object to InvoiceReportViewModel using simple Mapper.CreateMap like following:
public class InvoiceProfile: Profile
{
protected override void Configure()
{
Mapper.CreateMap<Invoice, InvoiceReportViewModel>()
.ForMember(c => c.CustomerName, op => op.MapFrom(v => v.CustomerName))
.ForMember(c => c.DiscountAmt, op => op.MapFrom(v => v.DiscountAmt))
.ForMember(c => c.InvoiceDate, op => op.MapFrom(v => v.InvoiceDate))
.ForMember(c => c.LineItems, op => op.MapFrom(v => v.InvoiceTransaction.TransactionItems));
Mapper.CreateMap<TransactionDetail, InvoiceReportLineItemViewModel>()
.ForMember(c => c.ProductName, op => op.MapFrom(v => v.Product.ProductName))
.ForMember(c => c.Qty, op => op.MapFrom(v => v.Qty))
//and so on;
}
}
Do not forget to register "InvoiceProfile"
Below is the Entity class
public partial class TestPlanViewModel
{
public TestPlanViewModel()
{
this.TestPlanTestPoints = new List<TestPlanTestPoint>();
}
public int TestPlanId { get; set; }
public string TestPlanName { get; set; }
public virtual IList<TestPlanTestPoint> TestPlanTestPoints { get; set; }
}
public class TestPlanTestPoint
{
public int OrganizationId { get; set; }
public int TestPlanId { get; set; }
public string TestPlanName { get; set; }
public int TestPlanVersion { get; set; }
public int TestPointId { get; set; }
public string TestPointName { get; set; }
}
I need to write a query where I need to get the collections from the dbContext like,
var query = (from tpm in TestPlanMaster
join tptp in TestPlanTestPoint on tpm.TestPlanId equals tptp.TestPlanId
join tmm in TestMethodMaster on tptp.TestMethodId equals tmm.TestMethodId
join tpma in TestPointMaster on tptp.TestPointId equals tpma.TestPointId
select new
{
//Plan Details
tpm.TestPlanId,
tpm.TestPlanName,
}).ToList().Select(x => new Entities.CustomEntities.TestPlanViewModel ====> Custom Entity
{
TestPlanId = x.TestPlanId,
TestPlanName = x.TestPlanName,
TestPlanTestPoints = ?????? ==> how to fill this collection
});
As shown above the TestPlanTestPoints is the IList collection object. I need to populate the data with the values from TestPlanTestPoint table.
Any suggestions?
Model
TestPlan
public partial class TestPlanMaster
{
[DataMember]
public int TestPlanId { get; set; }
[DataMember]
public short TestPlanVersion { get; set; }
[DataMember]
public int OrganizationId { get; set; }
[DataMember]
public short TestPlanTypeId { get; set; }
[DataMember]
public string TestPlanName { get; set; }
}
TestPlanTestPointMapping Model
public partial class TestPlanTestPointMapping
{
[DataMember]
public int OrganizationId { get; set; }
[DataMember]
public int TestPlanId { get; set; }
[DataMember]
public short TestPlanVersion { get; set; }
[DataMember]
public int TestPointId { get; set; }
}
You are thinking too much sql, think more linq. You want to select "tpm" items, so just write a select of those and select the new TestPlanViewModel and fill its properties and navigational properties.
I'm assuming you have a TestPlanTestPoints navigational property in your TestPlanMaster.
var q = (from tpm in TestPlanMaster
select new TestPlanViewModel
{
TestPlanId = tpm.TestPlanId,
TestPlanName = tpm.TestPlanName,
TestPlanPoints = TestPlanTestPoint.Where(pr => tpm.TestPlanId == pr.TestPlanId).Select(tptp => new TestPlanTestPoint()
{
// Fill properties here
}
}).ToList();
i have entity classes below,i want to a query like sql,how is that posssible with entity framework??
select * from SiteUsers su
inner join SiteUserRoles sur on su.Id=sur.SiteUserId
inner join SiteRoleActions sra on sur.SiteRoleId = sra.SiteRoleId
inner join SiteActions sa on sa.Id = sra.SiteActionId
where su.Id=1 and sa.ParentId=189
public class SiteRole
{
public SiteRole()
{
this.SiteActions = new HashSet<SiteAction>();
this.SiteUser = new HashSet<SiteUser>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<SiteAction> SiteActions { get; set; }
public virtual ICollection<SiteUser> SiteUser { get; set; }
}
public partial class SiteUser
{
public SiteUser()
{
this.SiteRoles = new HashSet<SiteRole>();
}
public int Id { get; set; }
public string Email { get; set; }
public string UserPassword { get; set; }
public string UserName { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public Nullable<System.DateTime> DateCreated { get; set; }
public virtual ICollection<SiteRole> SiteRoles { get; set; }
}
public partial class SiteAction
{
public SiteAction()
{
this.Childs = new HashSet<SiteAction>();
this.SiteRoles = new HashSet<SiteRole>();
}
public int Id { get; set; }
public string Name { get; set; }
public string ImagePath { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
public Nullable<int> ParentId { get; set; }
public Nullable<int> Type { get; set; }
public virtual ICollection<SiteAction> Childs { get; set; }
public virtual SiteAction Parent { get; set; }
public virtual ICollection<SiteRole> SiteRoles { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<SiteUser>().HasMany<SiteRole>(r => r.SiteRoles).WithMany(u => u.SiteUser).Map(m =>
{
m.ToTable("SiteUserRoles");
m.MapLeftKey("SiteUserId");
m.MapRightKey("SiteRoleId");
});
modelBuilder.Entity<SiteRole>().HasMany<SiteAction>(r => r.SiteActions).WithMany(u => u.SiteRoles).Map(m =>
{
m.ToTable("SiteRoleActions");
m.MapLeftKey("SiteRoleId");
m.MapRightKey("SiteActionId");
});
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
}
Since you don't have SiteUserRoles and SiteRoleActions as entities but only as join tables (if I read you well), you should use the navigation properties in stead of joining. Something like:
conext.SiteUsers.Where(su => su.id == 1
&& su.SiteRoles.SelectMany(sr => sr.SiteActions)
.Any(sa => sa.ParentId == 189 )
(Not syntax checked).
var result= (from su in SiteUsers
join sur in SiteUserRoles
on su.Id=sur.SiteUserId
join sra in SiteRoleActions
on sur.SiteRoleId = sra.SiteRoleId
join sa in SiteActions
on sra.SiteActionId=sa.Id
where su.Id=1 and sa.ParentId=189
select su).ToList();
Hope this will help . result will be a list of type SiteUsers.