Why I can't remove item from ObservableCollection? - c#

I filled some ObservableCollection<Employe> collection:
// Program.Data.Employees - it is ObservableCollection<Employe>.
Program.Data.Employees.Add(new Employe() { Name="Roman", Patronymic="Petrovich", Surname="Ivanov" });
Program.Data.Employees.Add(new Employe() { Name = "Oleg", Patronymic = "Vladimirovich", Surname = "Trofimov" });
Program.Data.Employees.Add(new Employe() { Name = "Anton", Patronymic = "Igorevich", Surname = "Kuznetcov" });
In other place of my code I try to remove some item from this collection:
// Program.Data.Employees - it is ObservableCollection<Employe>.
Employe x = Program.Data.Employees.First(n => n.Guid == emp.Guid); // x is not null.
Int32 index = Program.Data.Employees.IndexOf(x); // I got -1. Why?
Boolean result = Program.Data.Employees.Remove(x); // I got 'false', and item is not removed. Why?
// But this works fine:
Program.Data.Employees.Clear();
I can clear collection, but I can't remove necessary item. Why it happens?
UPD: Equals method of my Employe class
public bool Equals(Employe other) {
return
other.Guid == this.Guid &&
String.Equals(other.Name, this.Name, StringComparison.CurrentCultureIgnoreCase) &&
String.Equals(other.Patronymic == this.Patronymic, StringComparison.CurrentCultureIgnoreCase) &&
String.Equals(other.Surname == this.Surname, StringComparison.CurrentCultureIgnoreCase) &&
other.Sex == this.Sex &&
String.Equals(other.Post == this.Post, StringComparison.CurrentCultureIgnoreCase);
}

I tried the following code to reproduce your error:
class Employee
{
public string Name { get; set; }
public Guid Guid { get; set; }
}
// ...
ObservableCollection<Employee> employees = new ObservableCollection<Employee>();
var guid1 = Guid.NewGuid();
employees.Add(new Employee { Name = "Roman", Guid = guid1 });
employees.Add(new Employee { Name = "Oleg", Guid = Guid.NewGuid() });
var x = employees.First(e => e.Guid == guid1);
var index = employees.IndexOf(x); // index = 0, as expected
var result = employees.Remove(x); // result = true, as expected
It worked as expected. I would suggest, you set a breakpont at var x = ... and check, if
The collection really contains the item you're looking
If First() really returns that item
Then go to the next line and check, if index is returned correctly. And finally check again, if result is really false.
I see several possible causes of your code failing:
You didn't post the full code and something happens between x=Program.Data.Employees.First() and Program.Data.Employees.IndexOf()
You use multithreaded code (which also results in "something happening" between the two statements). In this case, you need to synchronize the access to the collection
You don't use a ObservableCollection directly but some derived class instead which is constructed by your data layer (such as DataServiceCollection, but this one should work fine too). In this case, check the actual type of your collection in the debugger
Another typical cause of errors with collection would be, if you try to remove items while iterating over the collection (i.e. inside a foreachloop): but in this case an exception should be thrown (and IndexOf should work fine), so this would only apply if you use some derived class which implements non-standard behaviour.
EDIT (in return to you posting your Equal method)
Your Equal method has a serious error in it:
String.Equals(other.Patronymic == this.Patronymic, StringComparison.CurrentCultureIgnoreCase)
... // applies also for following comparisons
should be
String.Equals(other.Patronymic, this.Patronymic, StringComparison.CurrentCultureIgnoreCase)
...
Also, if you're using a Guid, consider only comparing the GUIDs, since this usually means 'unique identifier', so it should be enough to identify some entity.

Related

How to make a join table search with Entity Framework?

So I made a windows form which has a search textbox that will return the parts of string that you have entered in the datagrid. However, in my attempt to code this following event. The datagrid shows boolean instead.
Which parts of the code is making all these result turns boolean and how can i fix this?
private void txtSearch_TextChanged(object sender, EventArgs e)
{
this.dataGridView1.DataSource = null;
this.dataGridView1.Rows.Clear();
using (var context = new edeappEntities1())
{
var data = context.bookingorders
.Join(
context.addressbooks,
booking => booking.addrID,
address => address.addrID,
(booking, address) => new
{
accID = booking.accID.Contains(txtSearch.Text),
bookId = booking.bookingID.Contains(txtSearch.Text),
companyName = address.companyName.Contains(txtSearch.Text),
address = address.addressLn1.Contains(txtSearch.Text) || address.addressLn2.Contains(txtSearch.Text) ||
address.addressLn3.Contains(txtSearch.Text),
region = address.region.Contains(txtSearch.Text),
postcode = address.postcode.Contains(txtSearch.Text),
contact = address.contectName.Contains(txtSearch.Text),
phone = address.phoneNo.Contains(txtSearch.Text),
fax = address.faxNo.Contains(txtSearch.Text),
telex = address.telexNo.Contains(txtSearch.Text),
pickupTime = booking.pickupDate.Contains(txtSearch.Text)
|| booking.pickupTime.Contains(txtSearch.Text)
}
).ToList();
foreach (var db in data)
{
dataGridView1.Rows.Add(db.accID, db.bookId, db.companyName, db.address, db.region,
db.postcode, db.contact, db.phone, db.fax, db.telex, db.pickupTime);
}
}
}
My modelling structure: model1.edmx
Search result is boolean: link
You are getting a Boolean result in all the columns because you are creating a new anonymous type and assigning the result of string.Contains() method to each property in that new anonymous type and string.Contains() returns a Boolean(bool).
For example, if I do this:
string str = "Hello!"
bool result = str.Contains("o");
Here, the Contains() method will return a Boolean value indicating whether the string contains the specified substring("o") in it. The return value here will be true which will be assigned to result.
In your code, you do something similar for each field:
accID = booking.accID.Contains(txtSearch.Text)
This will check if booking.accID contains the string searched by the user which is captured in txtSearch.Text. If your booking.accID contains txtSearch.Text, the method will return true and false if it does not contain the search text. This will create a new variable of type bool called accId and the return value will be stored in accId on the left-hand side of =.
Anonymous Types
In C#, an anonymous type is a quick way to create a wrapper object containing a set of properties without actually creating a class.
For instance, I want an object containing details about a person without creating a Person class, I can do this:
var myPerson = new { Name = "John", Age = 25, Salary = 10_000L };
Now, I have an object containing the properties Name, Age and Salary without even creating a Person class. The compiler creates a hidden class in the background. More on anonymous types here.
You are creating a lambda function that returns an anonymous type as the fourth parameter of the Join() method. This lambda function will be called on each result of the join operation.
Solution
The filtering condition should be specified in a Where() method instead of assigning it to properties in the anonymous type. The anonymous type should be used to capture and combine the two results:
var searchData = context
.bookingorders
.Join(
context.addressbooks,
booking => booking.addrID,
address => address.addrID,
(booking, address) => new
{
Booking = booking,
Address = address
})
.Where(data =>
data.Booking.bookingID.Contains(txtSearch.Text) ||
data.Address.companyName.Contains(txtSearch.Text) ||
data.Address.addressLn1.Contains(txtSearch.Text) ||
data.Address.addressLn2.Contains(txtSearch.Text) ||
data.Address.region.Contains(txtSearch.Text) ||
data.Address.postcode.Contains(txtSearch.Text) ||
data.Address.contectName.Contains(txtSearch.Text) ||
data.Address.phoneNo.Contains(txtSearch.Text) ||
data.Address.faxNo.Contains(txtSearch.Text) ||
data.Address.telexNo.Contains(txtSearch.Text) ||
data.Booking.pickupDate.Contains(txtSearch.Text) ||
data.Booking.pickupTime.Contains(txtSearch.Text)
)
.ToList();
foreach(var row in searchData)
{
dataGridView1.Rows.Add(
row.Booking.bookingId,
row.Address.companyName,
$"{row.Address.addressLn1} {row.Address.addressLn2}",
row.Address.region,
row.Address.postcode,
row.Address.contectName,
row.Address.phoneNo,
row.Address.faxNo,
row.Address.telexNo,
row.Booking.pickupDate,
row.Booking.pickupTime
);
}

dynamic fields sorting in elasticsearch and.net

I am trying to sort records based on dynamic field names sent to the search API.
the d19FilterCriteria object gives me the field name(SortOn) and the order(SortOrder) for sorting. I have used a sort descriptor for this purpose.
var sortDescriptor = new SortDescriptor<MPANStatus>();
// If Field Name is Dynamic
if (d19FilterCriteria.SortOrder == "asc")
{
sortDescriptor.Field(d19FilterCriteria.SortOn, Nest.SortOrder.Ascending);
}
else if (d19FilterCriteria.SortOrder == "desc")
{
sortDescriptor.Field(d19FilterCriteria.SortOn, Nest.SortOrder.Descending);
}
var result = await _elasticClient.SearchAsync<MPANStatus>(s => s
.Index("ampower-mpanstatusindex")
.Skip(skip)
.Size(pageSize)
.Sort(sort =>
{
sort = sortDescriptor;
return sort;
})
While debugging the sort descriptor shows me an object that has a valid value for Name and order
This query returns empty list for this code. Could I know what the issue here is?
You can have list of sort order based on your inputs:
List<ISort> sdBookSortOrder = new List<ISort>();
SortOrder oSortOrder = SortOrder.Ascending; //SortOrder.Descending;
sdBookSortOrder.Add(new FieldSort { Field = <sField1>, Order = oSortOrder });
sdBookSortOrder.Add(new FieldSort { Field = <sField2>, Order = oSortOrder });
And, you can use above sort order collection while sending search request - as shown below:
ISearchRequest searchRequest = new SearchRequest(SearchEngine.IndexName)
{
From = iFrom,
Size = iSize,
Query = query,
Sort = oSortOrder,
};
I hope, it will solve your problem.
Regards,
Nikunj
My guess is since you are using the dynamic mapping, the string will be considered as text field and sub field keyword
use field.keyword instead of just field
The better approach to work with dynamic sorting fields using NEST, it's using SortDescriptor
SortDescriptor<dynamic> sort = GetSorting(request.Sorting);
ISearchResponse result = elasticClient.Search<YourType>(s => s
.Index("your-index")
.Sort(s => sort));
Use this method to add a list of sorting
// method to generate the Sorting
public SortDescriptor<dynamic> GetSorting(List<Sort>? sorting)
{
SortDescriptor<dynamic> sortDescriptor = new();
if (sorting != null)
{
foreach (Sort sort in sorting)
sortDescriptor.Field(sort.Field, getOrder(sort.Order));
}
return sortDescriptor;
}
class "Sort" just to store Field and Order values
public string Field { get; set; }
public string Order { get; set; }

Catch read error on EF 6

I have a DB with no constraints (given, not changeable). My model look like
public MyModel
{
public long Id { get; set; }
// Even if database column 'Value' could be NULL,
// the model - from business view - could not.
public long Value { get; set; }
}
My data I'd like to read is
Id Value
1 1
2 2
3 NULL
4 4
When I read with with DBContext.MyModel.ToList() it fails, of course. Is there any possibility to catch the error on 3rd row and return the 3 valid ones?
I don't dependent on EF but I like an automatic mapping between DB an Code.
Update:
It seems I wasn't specific enough. I need the 3 rows AS WELL AS a notification for the error.
Additional I've created a simple case for demo. In real life I have around 800 tables with up to 250 columns. I can't catch anything by model modification like dates out of range, missing relationships and other stuff.
What I really need is a try..catch for every row or an event on row reading failure, something like this.
Ok, solved. Not very elegant, but functional.
var query = _DBContext
.Database
.SqlQuery<MyModel>("SELECT * FROM MyModel");
var result = new List<MyModel>();
var enumerator = query.GetEnumerator();
while (true)
{
try
{
var success = enumerator.MoveNext();
if (!success)
break;
var model = enumerator.Current;
result.Add(model);
}
catch (Exception ex)
{
}
}
return result;
You need to use nullable type
public MyModel
{
public long Id { get; set; }
// Even if database column 'Value' could be NULL,
// the model - from business view - could not.
public long? Value { get; set; }
}
And also, in your select query, you should exlude Value = null
var myModel = models.Where(x => x.Value != null);
Hope it helps.
I'm not entirely sure I understand what you're trying to do, but if you want to get a reflection of what's in the table, then why not make your model match the query? If Value can be NULL, then make Value nullable (i.e. define it as long?).
That way, you can simply do:
var records = DbContext.MyModel.ToList();
If you then want to filter out the NULLs, you can do:
records.Where(r => r.Value.HasValue)
And if you want the ones with NULLs you can do:
records.Where(r => !r.Value.HasValue)
Or if you want to know whether any row had a NULL you could do:
records.Any(r => !r.Value.HasValue)
Use the following code:
var list = from m in DBContext.MyModel
where (m != null)
select m;
And then just convert var list to a List of your choosing.
Edit 1
var myModel = models.Where(x => x.Value != null).ToList();
As kienct89 suggested might also work.
Edit 2
There are multiple options for "catching" the error
If you want to throw an exception just use this:
if(myList.Count() < DBContext.MyModel.Count()){
Exception myException = new Exception("Not all items ware correctly loaded");
throw myException;
}
OR create a seperate array with the faulty ones:
var faultyList = from m in DBContext.MyModel
where (m == null)
select m;
Or:
var faultyList= models.Where(x => x.Value == null).ToList();

Adding element to a list in class, which is a part of List<class>, based on condition

Let's have a simple class with 2 fields
public class Sample
{
public int IdOfSample;
public string SampleName;
}
And another using this one
public class ListOfSamples
{
public int IdOfList;
public List<Sample> SampleList;
}
And finally, since we will use a couple of different ListOfSamples, make a list of them:
public static List<ListOfSamples> FinalList = new List<ListOfSamples>();
Now the problem:
I create a new Sample (let's call it NewItem), with some name and Id. I want to check if there's a ListOfSamples in my FinalList that as the same Id as the NewItem I have. Otherwise create new ListOfSamples in the FinalList with the IdOfList = NewItem.IdOfSample.
I think I got the first part which checks if you should add a new list (ie. a ListOfSamples with specified IdOfList does not exist:
Sample NewItem = new Sample()
{
IdOfSample = 12345,
SampleName = "Some name"
};
int index = FinalList.FindIndex(f => f.IdOfList == NewItem.IdOfSample);
if (!FinalList.Any() || index == -1)
{
ListOfSamples NewList = new ListOfSamples()
{
IdOfList = NewItem.IdOfSample,
SampleList = new List<Sample>()
};
NewList.SampleList.Add(NewItem);
FinalList.Add(NewList);
}
Now, I'm trying to construct a statement, that, if the list with specified Id already exists in the FinalList, just add the new item to it, but so far I think my limited experience with LINQ is showing, nothing I try seems to work.
So:
If there exists a ListOfSamples with IdOfList == NewItem.IdOfSample in FinalList, then add NewItem to that ListOfSamples.
How about
if (!FinalList.Any() || index == -1)
...
else
{
FinalList[index].SampleList.Add(NewItem);
}
If you just wanted to check whether the list item existed, a suitable LINQ statement could be:
if (FinalList.Any(l => l.IdOfList == NewItem.IdOfSample))
{
// ...
}
Given you want to work on the item then you could attempt to retrieve it as follows:
var existingList = FinalList.SingleOrDefault(l => l.IdOfList == NewItem.IdOfSample);
if (existingList != null)
{
existingList.Add( ... );
}
Though perhaps it's worth thinking about using a HashSet of lists if you want to guarantee uniqueness...
if i understand it right ...
// search for the list with the given Id
var listOfSamples = finalList.Where(fl => fl.IdOfList == newItem.IdOfSample).FirstOrDefault();
if (listOfSamples == null)
{
// not found
// add new List with the new item in final list
finalList.Add(new ListOfSamples {IdOfList = newItem.IdOfSample, SampleList = new List<Sample>{newItem}} );
}
else
{
// found
// add the new item into the found list
listOfSamples.SampleList.Add(newItem);
}
If you replace ListOfSamples with a Dictionary<int, List<Sample>> then you will gain the ability to do a lookup in O(1) time and guarantee that the ids at the top level are unique. and then you can just add stuff like this.
Dictionary<int, List<Sample>> FinalList = new Dictionary<int, List<Sample>>();
Sample NewItem = new Sample()
{
IdOfSample = 12345,
SampleName = "Some name"
};
List<Sample> list;
if (!FinalList.TryGetValue(NewItem.IdOfSample, out list))
{
list = new List<Sample>();
FinalList.Add(NewItem.IdOfSample, list);
}
list.Add(NewItem);
TryGetValue will see if the dictionary has an entry for the key you pass it and returns true if it does and false if it does not. If it does have an entry for the key it also assigns the value of the entry (in this case your list of samples) to the out parameter. So, we check if it returns false and in that case we create a new list and add it to the dictionary. Then we add the sample to the list that we either got from the dictionary, or just created and put in the dictionary.

Cannot implicitly convert type '.List<AnonymousType#1>' to '.List<WebApplication2.Customer>'

In the following code that returns a list:
public List<Customer> GeAllCust()
{
var results = db.Customers
.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo })
.ToList()
return results;
}
I get an error reporting that C# can't convert the list:
Error: Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.List<WebApplication2.Customer>
Why is that?
Here's a screenshot showing some additional information that Visual Studio provides in a tooltip for the error:
Is it right way to return some columns instead of whole table....?
public object GeAllCust()
{
var results = db.Customers.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo }).ToList();
return results;
}
When you look the code:
x => new { ... }
This creates a new anonymous type. If you don't need to pull back only a particular set of columns, you can just do the following:
return db.Customers.ToList();
This assumes that Customers is an IEnumerable<Customer>, which should match up with what you are trying to return.
Edit
You have noted that you only want to return a certain subset of columns. If you want any sort of compiler help when coding this, you need to make a custom class to hold the values:
public class CustomerMinInfo
{
public string Name { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public int? ContactNumber { get; set; }
}
Then change your function to the following:
public List<CustomerMinInfo> GetAllCust()
{
var results = db.Customers.Select(x => new CustomerMinInfo()
{
Name = x.CustName,
Email = x.Email,
Address = x.Address,
ContactNumber = x.CustContactNo
})
.ToList();
return results;
}
This will work, however, you will lose all relationship to the database context. This means if you update the returned values, it will not stick it back into the database.
Also, just to repeat my comment, returning more columns (with the exception of byte arrays) does not necessarily mean longer execution time. Returning a lot of rows means more execution time. Your function is returning every single customer in the database, which when your system grows, will start to hang your program, even with the reduced amount of columns.
You are selecting to an anonymous type, which is not a Customer.
If you want to do (sort of) this, you can write it like this:
return db.Customers.Select(x => new Customer { Name = x.CustName, Email = x.CustEmail, Address = x.CustAddress, ContactNo = x.ContactNo }).ToList();
This assumes the properties on your Customer object are what I called them.
** EDIT ** Per your comment,
If you want to return a subset of the table, you can do one of two things:
Return the translated form of Customer as I specified above, or:
Create a new class for your business layer that only has only those four fields, and change your method to return a List<ShrunkenCustomer> (assuming ShunkenCustomer is the name that you choose for your new class.)
GetAllCust() is supposed to return a List of Customer, Select New will create a list of Anonymous Types, you need to return a list of Customer from your query.
try:
var results = db.Customers.Select( new Customer{CustName = x.CustName}).ToList(); //include other fields
I guess Customer is a class you have defined yourself?
The my suggestion would be to do something like the following:
var results = db.Customers.Select(x => new Customer(x.Custname, x.CustEmail, x.CustAddress, x.CustContactNo)).ToList();
The reason is that you are trying to return a list of Customer but the results from your link is an anonymous class containing those four values.
This would of course require that you have a constructor that takes those four values.
Basically whatever u got in var type, loop on that and store it in list<> object then loop and achieve ur target.Here I m posting code for Master details.
List obj = new List();
var orderlist = (from a in db.Order_Master
join b in db.UserAccounts on a.User_Id equals b.Id into abc
from b in abc.DefaultIfEmpty()
select new
{
Order_Id = a.Order_Id,
User_Name = b.FirstName,
Order_Date = a.Order_Date,
Tot_Qty = a.Tot_Qty,
Tot_Price = a.Tot_Price,
Order_Status = a.Order_Status,
Payment_Mode = a.Payment_Mode,
Address_Id = a.Address_Id
});
List<MasterOrder> ob = new List<MasterOrder>();
foreach (var item in orderlist)
{
MasterOrder clr = new MasterOrder();
clr.Order_Id = item.Order_Id;
clr.User_Name = item.User_Name;
clr.Order_Date = item.Order_Date;
clr.Tot_Qty = item.Tot_Qty;
clr.Tot_Price = item.Tot_Price;
clr.Order_Status = item.Order_Status;
clr.Payment_Mode = item.Payment_Mode;
clr.Address_Id = item.Address_Id;
ob.Add(clr);
}
using(ecom_storeEntities en=new ecom_storeEntities())
{
var Masterlist = en.Order_Master.OrderByDescending(a => a.Order_Id).ToList();
foreach (var i in ob)
{
var Child = en.Order_Child.Where(a => a.Order_Id==i.Order_Id).ToList();
obj.Add(new OrderMasterChild
{
Master = i,
Childs = Child
});
}
}

Categories

Resources