how can I swap two child entity values - c#

I have an entity called "Space" and inside it has "Images". Images has an ordering property that is an int. I'm trying to see if there is an easy linq that can be made to swap the ordering values.
public class Space
{
public int SpaceId { get; set; }
public virtual ICollection<SpaceImage> Images { get; set; }
}
public class SpaceImage
{
public int SpaceImageId { get; set; }
public byte[] Image { get; set; }
public byte[] ImageThumbnail { get; set; }
public string ContentType { get; set; }
public int Ordering { get; set; }
}
ex.
an image might have ordering 3 and another will have 6, I want to swap these two numbers for each of these images
public void Swap(int spaceId, int old, int new)
{
//swap 3 and 6 for the ordering value for the two spaceImages that contain these values where spaceId is spaceId
}

In general, LINQ would be used for querying. You could use it to find the appropriate SpaceImage instances, then set the values appropriately:
// Assuming this is a method in the Space class
public void Swap(int oldOrder, int newOrder)
{
var oldInst = this.Images.FirstOrDefault(si => si.Ordering == oldOrder);
var newInst = this.Images.FirstOrDefault(si => si.Ordering == newOrder);
if (oldInst != null && newInst != null)
{
oldInst.Ordering = newOrder;
newInst.Ordering = oldOrder;
}
else
{
// There weren't matching images - handle that case here
}
}

Related

Check if two instances of same class have same data in C#

I'm trying to validate if both my objects have the same values.
here's my class
public class myclass
{
public List<c1> List1{ get; set; }
public List<c2> List2{ get; set; }
public List<c3> List3{ get; set; }
}
public class c1
{
public int Number{ get; set; }
public string Name{ get; set; }
public bool IsAvailable{ get; set; }
}
public class c2
{
public int Id{ get; set; }
public string Text{ get; set; }
public GUID Guid{ get; set; }
}
public class c3
{
public int? Age{ get; set; }
public string Role{ get; set; }
public bool IsDeleted{ get; set; }
}
I have 2 instances of this class and I want to compare the data between both the instances and check if they both are the same or not.
I've tried Serializing both the objects and comparing but that's not working as the items in the list can be in a different order.
I've also tried getting HashSet of individual lists and checking if they are equal.
var s1 = new HashSet<c1>(list1);
var s2= new HashSet<c1>(list2);
return s1.SetEquals(s2);
One solution would be to first order the lists using a standard order, like numeric or alphabetic depending on the types, and then trying something like this
if (myclassInstance1.list1.SequenceEqual(myclassInstance2.list1))
{
//Repeat the cycle 2 more times and then //your code
}
Since this one works comparing the sequence you need to first order the lists and to do that you can maybe use this
EDIT: This is the documentation for the list.sequenceEquals() method
The best way to do this is if you explicitly implement the models with IEqualityComparer in .NET.
using System;
using System.Collections.Generic;
public class Program
{
public class MyClass
{
public List<C1> C1List { get; set; }
public List<C2> C2List { get; set; }
}
public class C1 : IEqualityComparer<C1>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Equals(C1 x, C1 y)
{
if(string.Equals(x.FirstName, y.FirstName, StringComparison.OrdinalIgnoreCase)
&& string.Equals(x.LastName, y.LastName, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
public int GetHashCode(C1 c)
{
return c.FirstName.Length * c.LastName.Length;
}
}
public class C2 : IEqualityComparer<C2>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Equals(C2 x, C2 y)
{
if(string.Equals(x.FirstName, y.FirstName, StringComparison.OrdinalIgnoreCase)
&& string.Equals(x.LastName, y.LastName, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
public int GetHashCode(C2 c)
{
return c.FirstName.Length * c.LastName.Length;
}
}
}
The IEqualityComparer will give you full control on "the parameters based on which you call 2 objects equal".
As for the above implementation, I have considered the 2 objects are equal if the FirstName and the LastName are same.
Try to order the list,and then compare the data in it:
List<c1> l1 = list1.OrderBy(c => c.Number).ToList();
List<c1> l2 = list2.OrderBy(c => c.Number).ToList();
bool equalValue = true;
if (l1.Count() == l2.Count()) {
for (int i = 0; i < l1.Count(); i++){
if (l1[i].IsAvailable != l2[i].IsAvailable || l1[i].Number != l2[i].Number || l1[i].Name != l2[i].Name)
{
equalValue = false;
break;
}
}
}
else {
equalValue = false;
}
return equalValue;

Empty string comparison is failing

Currently writing a simple reservation. Right now I have a multidimensional string array that holds the passengers name and I am trying to iterate through that array and see whether there are any open seats so I know whether or not to add them to a waiting list.
// Check how many seats are taken
for (int i = 0; i <= nameArray.GetUpperBound(0); i++)
{
for (int j = 0; j <= nameArray.GetUpperBound(1); j++)
{
if (nameArray[i, j] == "")
{
seatsFilled--;
}
else
{
seatsFilled++;
}
}
}
For some reason when I debug, I notice that this line
if (nameArray[i, j] == "")
Doesn't do anything at all and gets skipped over despite there being no names in the array. I cannot for the life of me figure out why. Any suggestions?
One of the reason i can see is the comparison may have the whitespace. Try this
if(string.IsNullOrWhiteSpace(nameArray[i, j]))
I would do something like this:
First I would create some interfaces and models that would define the structure of the seat. Make sure to account for seat restrictions.
public interface ISeat
{
int RowNumber { get; }
string SeatLetter { get; }
PassangerModel Passenger { get; }
}
We want to allow some of the seats to be restricted based on passenger age and height. Hard coding that into each seat is nasty so instead I created a SeatRestriction class that has a predicate in it. This predicate will take the passenger model in and return a result. So for example you could create a SeatRestriction that checked a passengers height or age to make sure they can open emergency doors.
public class SeatRestriction
{
public Predicate<PassangerModel> Restriction { get; private set; }
public SeatRestriction(Predicate<PassangerModel> restriction)
{
Restriction = restriction;
}
}
The passenger model can actually be much more verbose than this. I'm assuming here no dietary needs. Maybe this is a reservation system for Spirit Airlines.
public class PassangerModel
{
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string CreditCardName { get; set; }
public string CreditCardNumber { get; set; }
public string CreditCardExpiration { get; set; }
public string CreditCardSecurityPin { get; set; }
}
Next We come to our seat class. This class allows us to see if a passenger is assigned to the seat. Take note that it also asserts prior to adding the passenger to the seat if the passenger meets the restrictions, if any.
public interface IRestrictedSeat
{
List<SeatRestriction> Restrictions { get; }
bool AssertQualifiedPassenger(PassangerModel passenger);
}
public class Seat : ISeat, IRestrictedSeat
{
public bool IsSeatReserved { get { return Passenger != null; } }
public int RowNumber { get; private set; }
public string SeatLetter { get; private set; }
public PassangerModel Passenger { get; private set; }
public List<SeatRestriction> Restrictions { get; private set; }
public Seat(int rowNumber, string seatLetter)
{
Restrictions = new List<SeatRestriction>();
RowNumber = rowNumber;
SeatLetter = seatLetter;
}
public bool TryAddPassenger(PassangerModel passanger)
{
if (AssertQualifiedPassenger(passanger))
{
Passenger = passenger;
return true;
}
else
{
return false;
}
}
public bool AssertQualifiedPassenger(PassangerModel passenger)
{
foreach(SeatRestriction restriction in Restrictions)
{
if (!restriction.Restriction(passenger))
{
return false;
}
}
return true;
}
}
Finally we get to the row. Our row knows how manys seats it has and what its number is. You should be able to add seats to it and then call GetAvailableSeats to get the seats that are left in this row.
public class SeatRow
{
public int TotalSeats { get; private set; }
public int RowNumber { get; private set; }
public List<Seat> Seats { get; private set; }
public SeatRow(int seatsInRow, int rowNumber)
{
TotalSeats = seatsInRow;
RowNumber = rowNumber;
}
public bool TryAddSeatToRow(Seat seat)
{
if(Seats.Count <= TotalSeats && seat.RowNumber == RowNumber)
{
Seats.Add(seat);
return true;
}
else
{
return false;
}
}
public IEnumerable<Seat> GetAvailableSeats()
{
return Seats.Where(seat => seat.IsSeatReserved == false);
}
}
The long and short of this answer is there's a million ways to solve CIS problems and to not rely on stack overflow while you're in school/learning. Good luck and welcome to the club :-)
if (nameArray[i, j] == "")
Doesn't do anything at all and gets skipped over despite there being no names in the array.
That line is inside a loop that iterates through the items in the array. If the array is empty then this will never get executed.

Simple query performance

Let's say i have 4 Tables A,B,C and D
What is the best way to get list of B to be in a dropdown list.
Condition is that each record should have associated list of D1
so that if B has no D1 records i should not show it in the dropdown list
D1 is a child class of D
How i did it:
// number 2 to specify the selected A Table normally it's a variable
var b_Per_A = Uow.B.GetAll().Where(x => x.A_Id == 2).ToList();
var b_list = new List<B>();
foreach (var b in b_list)
{
var d =
Uow.D1.GetAll().Where(x => x.C.B_Id == b.Id);
if (!d.IsNullOrEmpty()) // extension method that checks if a collection is null or empty
{
b_list.Add(b);
}
}
It works but it's slow
UPDATE:
The signature of GetAll() is
IQueryable<T> GetAll();
UPDATE 2:
My Model is
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<B> ChildBs { get; set; }
}
public class B
{
public int Id { get; set; }
public string Name { get; set; }
public A ParentA { get; set; }
public IEnumerable<C> ChildCs { get; set; }
}
public class C
{
public int Id { get; set; }
public string Name { get; set; }
public B ParentB { get; set; }
public IEnumerable<D> ChildDs { get; set; }
}
public class D
{
public int Id { get; set; }
public string Name { get; set; }
public C ParentC { get; set; }
}
public class D1 : D
{
/// other properties
}
Given that you've confirmed you have navigation properties on your entity classes, this could be achieved in a single expression:
var result = Uow.B.GetAll()
.Where(b =>
b.ParentA.Id == 2 &&
b.ChildCs.Any() &&
b.ChildCs.SelectMany(c => c.ChildDs).Any())
.ToList();
This will push the filtering to your database rather than doing it in memory and should be a lot more efficient.
Also, I'm assuming that a parent navigation property will never be null and that child collections are always initialized.
Example here
put your var d = Uow.D.GetAll().Where(x => x.C.B_Id == b.Id); outside of your foreach. I also recommend you to use IEnumerable/ICollection instead of List and use for loop instead of foreach. Indexed looping is faster.

Multiple .Include in Repository

At first, here what I have:
4 Classes have some relations between them as:
Project has many Bridges.
Bridge has many Foundations.
Foundation has many PierCaps and many PierColumns.
Project.cs:
public class Project
{
public Project()
{
Bridges = new HashSet<Bridge>();
StartDate = DateTime.Now;
EndDate = DateTime.Now;
}
// Fields
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
// Calculated Fields
public int BridgeCount => Bridges.Count;
public int PierFoundationCount = 0;
public int PierCapCount = 0;
public int PierColumnCount = 0;
// Relation To Children
public virtual ICollection<Bridge> Bridges { get; private set; }
}
Bridge.cs:
public class Bridge : BasicBaseEntityWithName
{
public Bridge()
{
PierFoundations = new HashSet<PierFoundation>();
}
// Fields
public int ProjectID { set; get; }
public Project Project { set; get; }
// Calculated Fields
public int PierFoundationCount => PierFoundations.Count;
public int PierCapCount => PierFoundations.Count;
public int PierColumnCount => PierFoundations.Count;
// Relation To Children
public ICollection<PierFoundation> PierFoundations { get; private set; }
}
PierFoundation.cs:
public class PierFoundation : BasicBaseEntityWithName
{
public PierFoundation()
{
PierCaps = new HashSet<PierCap>();
PierColumns = new HashSet<PierColumn>();
}
// Fields
public float Length { set; get; }
// Calculated Fields
public int PierColumnCount => PierColumns.Count;
public int PierCapCount => PierCaps.Count;
public float FoundationQty => (Length * Width * Height);
// Relations To Parents
public int BridgeID { set; get; }
public Bridge Bridge { set; get; }
// Relations To Children
public ICollection<PierCap> PierCaps { get; set; }
public ICollection<PierColumn> PierColumns { get; set; }
}
PierColumn.cs:
public class PierColumn : BaseEntityNoName
{
// Fields
public float Diameter { set; get; }
// Relations To Parents
public int PierFoundationID { set; get; }
public PierFoundation PierFoundation { set; get; }
}
PierCap.cs:
public class PierCap : BaseEntityNoName
{
// Fields
public float CapWidth { set; get; }
// Relations To Parents
public int PierFoundationID { set; get; }
public PierFoundation PierFoundation { set; get; }
}
BaseEntityNoName.cs:
public class BaseEntityNoName
{
public BaseEntityNoName()
{
AddedBy = Core.General.Global.CurrentUseId;
AddedDate = DateTime.Now;
}
public int Id { get; set; }
public DateTime AddedDate { get; set; }
public int AddedBy { get; set; }
// Becuase they will not appear at new, they will be nullable
public DateTime? ModifiedDate { get; set; }
public int? ModifiedBy { get; set; }
}
BasicBaseEntityWithName.cs:
public class BasicBaseEntityWithName : BaseEntityNoName
{
public string Name { get; set; }
}
Adding to that, I have this method in my Repository:
public IEnumerable<TEntity> Include(params Expression<Func<TEntity, object>>[] includeExpressions)
{
DbSet<TEntity> dbSet = Context.Set<TEntity>();
IEnumerable<TEntity> query = null;
foreach (var includeExpression in includeExpressions)
{
query = dbSet.Include(includeExpression);
}
return query ?? dbSet;
}
My questions are:
Q1. When I use Include the related entities (PierFoundation, PierCap, PierColumn), I don't get them all!. So, when I type:
PierFoundations = CurrentUnitOfWork.PierFoundationRepository.Include(x => x.PierColumns, x => x.PierCaps);
I received just PierFoundation and PierCap, but not PierColumn
What is the wrong here?. I want to retrieve all data in the all three of them.
Q2. In the Project class, I have four calculate fields:
BridgeCount which is solved by Bridges.Count
PierFoundationCount
PierCapCount
PierColumnCount
The last three, I don't know how to get them, because they don't have direct relation with Project
BTW: I am using EF Core 2
The two issues are unrelated, so answering only the main (as indicated by the post title) Q1:
When I use Include the related entities (PierFoundation, PierCap, PierColumn), I don't get them all!.
It's caused by the following line in your repository method implementation:
query = dbSet.Include(includeExpression);
As with many LINQ method, it's important to use the Include method return value when chaining with other calls, and as you can see, this code stores only the last Include call, hence the behavior you are observing.
It can be fixed by changing the implementation like this:
var query = Context.Set<TEntity>().AsQueryable();
foreach (var includeExpression in includeExpressions)
query = query.Include(includeExpression); // Note the usage of query variable
return query;
or simply
return includeExpresions.Aggregate(Context.Set<T>().AsQueryable(), (q, e) => q.Include(e));
But note that EF Core uses different approach for including nested data levels (ThenInclude) which cannot be expressed with Expression<Func<T, object>>, so you might consider exposing IQueryable<TEntity> returning method from your repository and use the EF Core provided Include / ThenInclude extension methods.
What about Q2, consider posting it in a separate SO question (post).

How to group multiple lists using LINQ - Sum and GroupBy

I have three generic lists and I am trying to combine and get them into one (List<One>). I am not sure how to use LINQ group by on different lists. I want to group by AccountCode, AccountDate, and SalesCode; and sum the amount.
public class One
{
public string AccountCode { get; set; }
public DateTime AccountDate { get; set; }
public string SalesCode { get; set; }
public decimal? Amount { get; set; }
}
public class Two
{
public string AccountCode { get; set; }
public DateTime AccountDate { get; set; }
public string SalesCode { get; set; }
public decimal? Amount { get; set; }
}
public class Three
{
public string AccountCode { get; set; }
public DateTime AccountDate { get; set; }
public string SalesCode { get; set; }
public decimal? Amount { get; set; }
}
List<One> oneList = new List<One>();
List<Two> twoList = new List<Two>();
List<Three> threeList = new List<Three>();
This is the sample query I have, which is not working. Note: I have not included List<Three>.
from first in oneList
join second in twoList
on first.AccountCode equals second.AccountCode
where
(first.AccountDate.Date == second.AccountDate.Date && first.SalesCode == second.SalesCode)
select new
{
first.AccountCode,
first.AccountDate,
first.SalesCode
first.Amount,
second.Amount
}).Distinct()
group bill by new { bill.AccountCode, bill.AccountDate, bill.SalesCode }
into groupedList
select new
{
groupedList.Key.UAN,
groupedList.Key.AccountDate,
Amount = groupedList.Sum(a => a.Amount)
};
As mentioned in the comment, the first thing you'll need is a common type so that the 3 lists can be joined into a single list. That could be a base class or an interface as I've shown here.
public interface IAccount
{
string AccountCode { get; set; }
DateTime AccountDate { get; set; }
string SalesCode { get; set; }
decimal? Amount { get; set; }
}
public class One : IAccount
{
// ...
}
public class Two : IAccount
{
// ...
}
public class Three : IAccount
{
// ...
}
Once that's in place, then it's easy to combine your 3 lists into one like this:
var allList = oneList.Cast<IAccount>().Union(twoList).Union(threeList);
From there, the group by becomes much simpler as well. When using LINQ to group by multiple fields, you'll want an IEqualityComparer class that looks like this:
public class AccountComparer : IEqualityComparer<IAccount>
{
public bool Equals(IAccount x, IAccount y)
{
return x.AccountCode == y.AccountCode &&
x.AccountDate == y.AccountDate &&
x.SalesCode == y.SalesCode;
}
public int GetHashCode(IAccount obj)
{
return (obj.AccountCode + obj.AccountDate + obj.SalesCode).GetHashCode();
}
}
The group by call is then one line:
var groups = allList.GroupBy(a => a, new AccountComparer());
Alternatively, you could create an AccountKey class that only has the 3 key fields and return that from the keySelector lambda. That feels a little cleaner, but is a bit more code.
From there, you can select the sums like this:
var amountSums = from g in groups
select new
{
g.Key.AccountCode,
g.Key.AccountDate,
g.Key.SalesCode,
Amount = g.Sum(a => a.Amount)
};

Categories