I am trying to use Linq Union to add additional record into result but Union do not work. Maybe someone could point me in right direction.
public class ProductView
{
public int Id { get; set; }
public bool Active { get; set; }
public string Name { get; set; }
public int ProductTypeId { get; set; }
public int UserCount { get; set; }
}
void Main()
{
var product = Products.Select(p => new ProductView
{
Id = p.Id,
Active = p.Active,
Name = p.Name,
ProductTypeId = p.ProductTypeId,
UserCount = 1
}).ToList();
//The new item is not jointed to the result above
product.Union(new[] {
new ProductView
{
Id = 9999,
Active = true,
Name = "Test",
ProductTypeId=0,
}
});
product.Dump();
}
You need to store the output:
var product2 = product.Union(new[] {
new ProductView
{
Id = 9999,
Active = true,
Name = "Test",
ProductTypeId=0,
}
});
product2.Dump();
In addition to this, overriding the Equals behaviour would be useful - as you probably want to check equality using just the Id field?
For example, if you don't override the Equals behaviour, then you will get Object reference equals like this:
void Main()
{
var list = new List<Foo>()
{
new Foo() { Id = 1},
new Foo() { Id = 2},
new Foo() { Id = 3},
};
var list2 = new List<Foo>()
{
new Foo() { Id = 1},
new Foo() { Id = 2},
new Foo() { Id = 3},
};
var query = list.Union(list2);
query.Dump();
}
// Define other methods and classes here
public class Foo
{
public int Id {get;set;}
}
produces six items!
But if you change Foo to:
public class Foo
{
public int Id {get;set;}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Foo)) return false;
var foo= (Foo)obj;
return this.Id == foo.Id;
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
then you will get 3 items - which is probably what you are expecting.
You need to override Equals and GetHashCode in ProductView if you want to use Union in a meaningful way(other than comparing by refence).
public class ProductView
{
public int Id { get; set; }
public bool Active { get; set; }
public string Name { get; set; }
public int ProductTypeId { get; set; }
public int UserCount { get; set; }
public override bool Equals(object obj)
{
if (obj == null || !(obj is ProductView)) return false;
ProductView pv2 = (ProductView)obj;
return this.Id == pv2.Id;
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
You could also implement an IEqualityComparer<ProductView> in a similar way and use it for this overload of Union.
Related
public class Order
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public bool MarkAsPresent { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int OrderItemId { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public Order Order { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
Please someone can help? From ASP.NET Core API controller, I need to return the following JSON. Please note that in the OrderItems, it includes the Product.ProductName. Also, if the Order.MarkAsPresent is true, then the productName should be replaced as 'Gift' in the JSON data.
{
"order": {
"orderId": "123",
"orderDate": "01-May-2021",
"orderItems": [
{ "productName": "Pen", "quantity": "25", "retailPrice": "3.50" },
{ "productName": "Paper", "quantity": "500", "retailPrice": "5.50" },
]
}
}
The two answers given so far are far too cumbersome. There's a very common, standard way to do this:
var result = from o in context.Order
select new
{
o.OrderId,
o.OrderDate,
OrderItems =
from oi in o.OrderItems
select new
{
ProductName = o.MarkAsPresent
? "Gift"
: oi.Product.ProductName,
oi.Quantity,
RetailPrice = oi.Price
}
};
You didn't define RetailPrice, so I assume it's just OrderItem.Price. If you like, the two select new statement can be replaced by projections into named DTO classes like OrderDto and OrderItemDto, respectively.
You can do it using an OrderDto.
public class OrderDto
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public object OrderItems { get; set; }
public OrderDto(int orderId, DateTime orderDate)
{
OrderId = orderId;
OrderDate = orderDate;
}
}//Cls
//--------------------------------------------//
public class OrderItemDto
{
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal RetailPrice { get; set; }
public OrderItemDto(string giftName, int quantity, decimal retailPrice)
{
ProductName = giftName;
Quantity = quantity;
RetailPrice = retailPrice;
}
}//Cls
//--------------------------------------------//
public class GiftDto
{
public string GiftName { get; set; }
public int Quantity { get; set; }
public decimal RetailPrice { get; set; }
public GiftDto(string giftName, int quantity, decimal retailPrice)
{
GiftName = giftName;
Quantity = quantity;
RetailPrice = retailPrice;
}
}//Cls
Add this method to your Order class
public class Order
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public bool MarkAsPresent { get; set; }
public List<OrderItem> OrderItems { get; set; }
public OrderDto ToDto()
{
var dto = new OrderDto(OrderId, OrderDate);
if (MarkAsPresent)
dto.OrderItems = OrderItems.Select(oi => new GiftDto(oi.Product.ProductName, oi.Quantity, oi.Price));
else
dto.OrderItems = OrderItems.Select(oi => new OrderItemDto(oi.Product.ProductName, oi.Quantity, oi.Price));
return dto;
}
}//Cls
Then in your action return something like Ok(order.ToDto());
Examples:
[HttpGet]
[AllowAnonymous]
public IActionResult OrderGift()
{
var order = new Order()
{
OrderId = 1,
OrderDate = DateTime.Now,
MarkAsPresent = true
};
order.OrderItems = new List<OrderItem>();
for (int i = 0; i < 5; i++)
{
order.OrderItems.Add(
new OrderItem()
{
Order = order,
OrderId = order.OrderId,
OrderItemId = i + 10,
Price = (i + 1) * 10,
Product = new Product()
{
ProductId = i + 20,
ProductName = $"Name {i}"
},
ProductId = i + 20,
Quantity = i * 2 + 4
});
}//for
return Ok(order.ToDto());
}///OrderGift
[HttpGet]
[AllowAnonymous]
public IActionResult OrderRegular()
{
var order = new Order()
{
OrderId = 1,
OrderDate = DateTime.Now,
MarkAsPresent = false
};
order.OrderItems = new List<OrderItem>();
for (int i = 0; i < 5; i++)
{
order.OrderItems.Add(
new OrderItem()
{
Order = order,
OrderId = order.OrderId,
OrderItemId = i + 10,
Price = (i + 1) * 10,
Product = new Product()
{
ProductId = i + 20,
ProductName = $"Name {i}"
},
ProductId = i + 20,
Quantity = i * 2 + 4
});
}//for
return Ok(order.ToDto());
}//OrderRegular
You can also use the Linq's let clause:
var report =
from o in orders
let orderItems =
from oi in o.OrderItems
select new {
productName = o.MarkAsPresent ? "Gift" : oi.Product.ProductName,
quantity = oi.Quantity,
retailPrice = oi.Price
}
select new {
orderId = o.OrderId,
orderDate = o.OrderDate,
orderItems
};
Use join to join the two models so we can connect every properties, and group all the
data by
OrderId and OrderDate, then just select the property you want to show.
About the gift,you can use if else in select.
Following is my demo:
[HttpGet]
public JsonResult Get()
{
var model = (from e in _context.OrderItems.Include(x=>x.Order).ToList()
join y in _context.Products.ToList() on e.OrderItemId equals y.ProductId
group new { e, y } by new { e.OrderId, e.Order.OrderDate } into g
select new
{
order = new
{
orderId = g.Key.OrderId,
orderDate = g.Key.OrderDate,
orderItems = (
from t in g.ToList()
select new
{
productName = t.e.Order.MarkAsPresent == true ? "Gift" : t.y.ProductName,
quantity = t.e.Quantity,
retailPrice = t.e.Price
}
).ToList()
}
}).ToList();
return new JsonResult(model);
}
Result:
I use to add value from the VUEJS where write code like this.
<multiselect v-model="schoolTypeform.schoolTypeId" :options="SchoolTypes" :multiple="true" :close-on-select="false" :clear-on-select="false" :preserve-search="true" placeholder="Pick School Type" label="name" track-by="name" :preselect-first="true">
and the JS code for this is written like this:
async addSchool() {
this.isbtnLoading = true;
this.isException = false;
await this.axios.post(this.school, this.form).then(response => {
this.addSchoolType(response.data);
})
},
async addSchoolType(id) {
this.isbtnLoading = true;
this.isException = false;
this.schoolTypeform.shoolId = id;
await this.axios.post(this.apiBasedUrl + '/SchoolsSchoolType', this.schoolTypeform).then(response => {
this.isbtnLoading = false;
});
Now my ER structure is given like this:
School:(Table1)
public partial class Schools
{
public Guid ID { get; set; }
public string Name{ get; set; }
// Navigation
public ICollection<SchoolsSchoolType> SchoolsSchoolTypes { get; set; }
}
SchoolType:(Table2)
public class SchoolType
{
public Guid Id { get; set; }
public string Name { get; set; }
//Navigation
public ICollection<SchoolsSchoolType> SchoolsSchoolTypes { get; set; }
}
SchoolsSchoolType (It is Intermediate table): Here the relation between the above is many to many.
public class SchoolsSchoolType
{
public Guid Id { get; set; }
public Guid ShoolId { get; set; }
public Schools Schools { get; set; }
public Guid SchoolTypeId { get; set; }
public SchoolType SchoolType { get; set; }
}
Here is repository method write for single value input, but I want to add here multiple value in the intermediates or junction table.
public async Task<Guid> CreateSchoolsAsync(SchoolsCreateVm schoolsCreateVm)
{
if (_GpsContext != null)
{
var schoolsEntity = new Schools()
{
ID = Guid.NewGuid(),
Name = schoolsCreateVm.Name,
SchoolsSchoolTypes = new List<SchoolsSchoolType>()
};
var schoolType = new SchoolType();
schoolsEntity.SchoolsSchoolTypes = new List<SchoolsSchoolType>
{
new SchoolsSchoolType
{
ShoolId =schoolsEntity.ID,
SchoolTypeId =schoolType.Id
}
};
return schoolsEntity.ID;
}
return Guid.Empty
}
Controller code is written here:
[HttpPost]
public async Task<IActionResult> PostSchool([FromBody]SchoolsCreateVm schoolsCreateVm)
{
var result = await _schoolsRepository.CreateSchoolsAsync(schoolsCreateVm);
if (result != null)
{
return Ok(result);
}
return NotFound();
}
Here is viewmodel used by me:
public class SchoolsCreateVm
{
public string Name { get; set; }
public List<Guid> SchoolTypeId{ get; set; } // List type of for intermediate table
public SchoolsCreateVm()
{
SchoolTypeId = new List<Guid>();
}
How can insert many schooltype for a single school in the intermediates(many to many) relation table through the VUEJS multiple selects.
Finally I am able to find the solution...
public async Task<Guid> CreateSchoolsAsync(SchoolsCreateVm schoolsCreateVm)
{
if (_GpsContext != null)
{
var schoolId = Guid.NewGuid();
var schoolsEntity = new Schools()
{
ID = schoolId, // 1--[1,2,3]
Name = schoolsCreateVm.Name,
};
// Here the code in which we can enter in the multiple table and Intermediate table
var SchoolsSchoolTypeList = new List<SchoolsSchoolType>();
foreach(var item in schoolsCreateVm.SchoolTypeId)
{
SchoolsSchoolTypeList.Add(new SchoolsSchoolType
{
Id = Guid.NewGuid(),
ShoolId = schoolId,
SchoolTypeId = item,
});
}
await _GpsContext.School.AddAsync(schoolsEntity);
_GpsContext.SchoolsSchoolTypes.AddRange(SchoolsSchoolTypeList);//Enter here for intermediate table that is 'SchoolsSchoolTypes'
await _GpsContext.SaveChangesAsync();
return schoolsEntity.ID;
}
return Guid.Empty;
}
I want to get the sum of applicants that applied to a specific position, this should not be saved as a column.
My model is simple:
We have positions:
net developer
java developer
We have applicants:
Luis
John
etc
We have applicants per position
With this column or property I want to know how many people have applied to each position, depending on the status.
So in my mvc view I want to show something like:
Position Applied Accepted Rejected ... other status
.net developer 5 3 2
java developer 3 2 1
The real problem here is the linq query which I am not very expert.
EDIT: I think I needed to change where the linq query must be coded, I suppose it should be in the ApplicantPosition class instead of Position, I also changed the types of Position and Application to be ICollection.
Please see the modified code.
public class Position
{
public int id { get; set; }
[StringLength(20, MinimumLength=3)]
public string name { get; set; }
public int yearsExperienceRequired { get; set; }
}
public class Applicant
{
public int ApplicantId { get; set; }
[StringLength(20, MinimumLength = 3)]
public string name { get; set; }
public string telephone { get; set; }
public string skypeuser { get; set; }
public ApplicantImage photo { get; set; }
}
public class ApplicantPosition
{
public virtual ICollection<Position> appliedPositions { get; set; }
public virtual ICollection<Applicant> applicants { get; set; }
public DateTime appliedDate { get; set; }
public int StatusValue { get; set; }
public Status Status
{
get { return (Status)StatusValue; }
set { StatusValue = (int)value; }
}
[NotMapped]
public int numberOfApplicantsApplied
{
get
{
var query =
from ap in appliedPositions
select new
{
positionName = g.Key.name,
peopleApplied = g.Count(x => x.Status == Status.Applied),
};
return query.Count(); ---??
}
}
}
Use direct SQL with PIVOT operator. This is really not a case for Linq query.
You can paste this into LINQPad as C# Program and run.
public enum Status
{
Applied,
Accepted,
Rejected
}
public class Position
{
public int id { get; set; }
public string name { get; set; }
}
public class Applicant
{
public int ApplicantId { get; set; }
public string name { get; set; }
}
public class ApplicantPosition
{
public Position appliedPosition { get; set; }
public Applicant applicant { get; set; }
public DateTime appliedDate { get; set; }
public Status Status { get; set; }
}
void Main()
{
var p1 = new Position { id = 1, name = ".net developer" };
var p2 = new Position { id = 2, name = "java developer" };
var a1 = new Applicant { ApplicantId = 100, name = "Luis" };
var a2 = new Applicant { ApplicantId = 200, name = "John" };
var ap1 = new ApplicantPosition { appliedPosition = p1, applicant = a1, Status = Status.Applied };
var ap2 = new ApplicantPosition { appliedPosition = p1, applicant = a2, Status = Status.Accepted };
var ap3 = new ApplicantPosition { appliedPosition = p2, applicant = a2, Status = Status.Rejected };
var db = new[] { ap1, ap2, ap3};
var query =
from ap in db
group ap by ap.appliedPosition into g
select new
{
positionName = g.Key.name,
peopleApplied = g.Count(x => x.Status == Status.Applied),
peopleAccepted = g.Count(x => x.Status == Status.Accepted),
peopleRejected = g.Count(x => x.Status == Status.Rejected),
};
query.Dump();
}
The result will be:
positionName peopleApplied peopleAccepted peopleRejected
.net developer 1 1 0
java developer 0 0 1
According to my experiences you can use LinQ or Entity Framework just by mapping your tables in to a DBML to a Entity Framework Model file.
In other way Microsoft gives you a Dynamic LinQ class that you can use it.I think you map all your columns and user Dynamic LinQ class.Good luck
I have two list of different columns, but each list have a common column with the same key, how do I combine them into a new list, i.e:
public class TradeBalanceBreak
{
public int CommID { get; set; }
public int CPFirmID { get; set; }
public double CreditDfferenceNotional { get; set; }
public string Currency { get; set; }
}
public class Commission
{
public int CommID { get; set; }
public PeriodStart { get; set; }
public ResearchCredit { get; set; }
}
public class CommissionList
{
public List<Commission> Commissions { get { return GetCommissions(); }}
private List<Commission> GetCommissions()
{
// retrieve data code ... ...
}
}
public class TradeBalanceBreakModel
{
public List<TradeBalanceBreak> TradeBalanceBreaks { get; set; }
}
public class CommissionModel
{
public List<CommissionList> CommissionLists { get; set; }
}
What I would like to achieve is to combine/flatten the TradeBalancesBreaks and CommissionLists (from the model classes) into one. The CommID is shared between the two.
Thanks.
Using Join (extension method version) -- after your update
var list1 = GetTradeBalanceBreaks();
var list2 = new CommisionsList().Commissions;
var combined = list1.Join( list2, l1 => l1.ID, l2 => l2.First().ID,
(l1,l2) = > new
{
l1.CommID,
l1.CPFirmID,
l1.CreditDifferenceNotional,
l1.Currency,
PeriodStarts= l2.SelectMany( l => l.PeriodStart ),
ResearchCredits = l2.SelectMany( l => l.ResearchCredit )
})
.ToList();
var combined = from p in PhoneNumbers
join a in Addresses on a.ID equals p.ID
select new {
ID = p.ID,
Name = p.Name,
Phone = p.Phone,
Address = a.Address,
Fax = a.Fax
};
I'm working with the new EF4 CTP4 although I don't think this has much to do with that. I am trying to set up a system where I can add auditable fields for our database automatically. What I'm trying to do is combine the following two expressions
a => new
{
a.CreatedBy,
a.CreatedTime,
a.UpdatedBy,
a.UpdatedTime
}
and
a => new
{
a.Id,
a.Name,
}
so the result is equivalant to
a => new
{
a.Id,
a.Name,
a.CreatedBy,
a.CreatedTime,
a.UpdatedBy,
a.UpdatedTime
}
the result I need to be an Expression<Func<T, object>>. I've been poking around and tried several things with Expression.Invoke and Expression.And(andalso) and haven't found anything that is working for me.
I'm not quite sure if this is possible but any help would be appreciated.
I don't think you can simply 'merge' two expressions. But you can use alternate API to create mappings with EntityMap.
public static class MapBuilder
{
public static Expression<Func<T, object>> GetMap<T>(Expression<Func<T, object>> func) where T: IAuditable
{
var body = func.Body as NewExpression;
var param = Expression.Parameter(typeof(T), "o");
var propertyAccessExprs = new List<Expression>();
foreach (MemberInfo member in body.Members)
{
propertyAccessExprs.Add(Expression.Property(param, member.Name));
}
var props = typeof(IAuditable).GetProperties();
foreach (PropertyInfo prop in props)
{
propertyAccessExprs.Add(Expression.Property(param, prop.Name));
}
var columnMappins = new List<Expression>();
foreach (var access in propertyAccessExprs)
{
columnMappins.Add(Expression.Call(typeof(EntityMap).GetMethod("Column", new Type[] {typeof(Object)}), Expression.Convert(access, typeof(Object))));
}
var RowExpr = Expression.Call(typeof(EntityMap).GetMethod("Row"), Expression.NewArrayInit(typeof(EntityMapColumn), columnMappins));
var result = Expression.Lambda<Func<T, object>>(RowExpr, param);
return result;
}
}
The usage is
var builder = new ModelBuilder();
builder.Entity<SimpleAuditableObject>()
.HasKey(o => o.Id)
.MapSingleType(MapBuilder.GetMap<SimpleAuditableObject>(o => new { o.Id, o.Name }));
Where
public interface IAuditable
{
int CreatedBy { get; set; }
DateTime CreatedTime { get; set; }
int UpdatedBy { get; set; }
DateTime UpdatedTime { get; set; }
}
public class SimpleAuditableObject : IAuditable
{
public int Id { get; set; }
public string Name { get; set; }
public int CreatedBy { get; set; }
public DateTime CreatedTime { get; set; }
public int UpdatedBy { get; set; }
public DateTime UpdatedTime { get; set; }
}
HTH.