how can i write more simple code without new select? [duplicate] - c#

This question already has answers here:
Delete entities without loading them into memory
(5 answers)
Closed 2 years ago.
public async Task DeleteAsync(int id)
{
Food food = await dataContext.Foods.FindAsync(id);
dataContext.Foods.Remove(food);
await dataContext.SaveChangesAsync();
}
it is CRUD operation for food model. I want to do it without new select.

Remove needs an object to remove. So if you want to use Remove, you have to select (or create an object and set the ID as in the answer of Максим Кошевой) the object in your case.
If you want to remove directly, you can type the query like this :
dataContext.Database.ExecuteSqlCommand("DELETE FROM Foods WHERE ID = {0}", id);
//Assuming the ID field's named ID in your Foods table and the table is named Foods (in some cases EF can add s at the end of tables).

You don't actually need all the fields to delete a record. You only need id. So you can create a simple stub that would only contain id and pass it into Remove method.
public async Task DeleteAsync(int id)
{
var food = new Food { Id = id };
dataContext.Foods.Remove(food);
await dataContext.SaveChangesAsync();
}
You can find more information about it here: https://www.learnentityframeworkcore.com/dbcontext/deleting-data

Related

Slapper only maps one object when selecting multiple objects from database

I have some C# classes which represent database objects, some of which contain one or more other custom objects or enumerables of custom objects. I'm using dapper for queries, and slapper to map to the custom objects. It works great for single object. I can easily grab a parent object with a specific ID from the database, do some inner joins, and map it and all the things it "owns" to my custom objects in C#. Problem comes when I want to do a select over multiple parent-IDs.
Some context, let's say I have a person, that person has a list of hobbies which have an ID and a Description, a list of days they're available which also have an ID and Description, and maybe another custom field such as whether they have or are even willing to be around children which can also boil down to a simple ID and Description. We'll call that last field child status. I'd write a select statement like this:
SELECT
,person.id as Id
,person.first_name as FirstName
,person.last_name as LastName
,hobby.Id as Hobbies_Id
,hobby.Description as Hobbies_Description
,avail.Id as Availabilities_Id
,avail.Description as Availabities_Description
,child.Id as ChildStatus_Id
,child.Description as ChildStatus_Description
FROM
users.users person
JOIN
users.userhobbies uhobby
ON
person.id = uhobby.UserId -- one-to-many with relational table
JOIN
users.avail hobby
ON
uhobby.HobbyId = hobby.Id
JOIN
users.useravailabilities uavail
ON
person.id = uavail.UserId -- one-to-many with relational table
JOIN
users.availabilities avail
ON
uavail.AvailId = avail.Id
JOIN
users.childstatuses child
ON
person.ChildStatusId = child.Id
Then I want this mapped to a user like this:
class User
{
public Guid Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public IEnumerable<Hobby> Hobbies {get; set;}
public IEnumerable<Availability> Availabilities {get; set;}
public ChildStatus ChildStatus {get; set;}
}
Since I'm using exact naming conventions and everything here, the query from Dapper and the Automapping work great just like this:
// Using the above sql in a variable
var data = Connection.Query<dynamic>(sql);
var dataReal = Slapper.AutoMapper.MapDynamic<User>(data);
return dataReal;
This works great, but it only ever returns one user. I have a similar method which takes an ID and all of my test users can be retrieved perfectly by passing the ID. I've tried scouring the internet, looking through documentation, and all I found was this: https://github.com/SlapperAutoMapper/Slapper.AutoMapper/issues/57 who seemed to just slip through the cracks. I also tried mapping the dynamic data to various other structures with no luck. Thanks in advance!
Update:
I've come up with a somewhat brutal, "sledgehammer" type solution. I'm not sure if, at this point, I'm forcing myself to use Slapper when there might be a more convenient solution. However, I wanted to ensure anyone in a similar situation might have a chance at making it work. Here's the new C# section:
var data = Connection.Query<dynamic>(sql);
IEnumerable<Guid> Ids = data.Select(row => (Guid)row.id).Distinct();
List<User> results = new List<User>();
foreach (Guid Id in Ids)
{
IEnumerable<dynamic> rows = data.Where(x => { return ((Guid) x.id).Equals(Id); });
User model = (Slapper.AutoMapper.MapDynamic<User>(rows, false) as IEnumerable<User>).FirstOrDefault();
if (model != null)
{
results.Add(model);
}
}
return results;
As you can see, I'm generating a list of unique "primary object" ID's and selecting those rows into their own lists, which I then pass to Slapper. I've passed the "cache = false" parameter to avoid squeezing unrelated data into every object after the first. I could probably get around this by actually keeping the UserHobby/UserAvailability/UserPhoto Ids in place, but I don't like the way that makes my object look. Hopefully this helps someone.
I'm not familiar with Slapper, but I'll show you what I've done with Dapper to construct a complex graph of objects with bi-directional references.
In short, construct a Dictionary or KeyedCollection prior to calling connection.Query<>, then reference it inside the Dapper lambda expression.
This method returns a list of service calls. Each service call is assigned to one technician and one customer. However, a technician may be assigned multiple service calls to multiple customers. And a customer may have multiple technicians on-site.
public ServiceCallResponse GetServiceCallsDapper(ServiceCallRequest Request)
{
var queryParameters = new {statuses = Request.Statuses, createDate = Request.CreateDate};
const string splitOn = "Number,Id"; // Id indicates beginning of second class (Technician). Number indicates begining of third class (Customer).
// Note multiple columns are named "Number". See note below about how Dapper maps columns to class properties.
// Note Dapper supports parameterized queries to protect against SQL injection attacks, including parameterized "where in" clauses.
const string query = #"sql query here..."
ServiceCallResponse response = new ServiceCallResponse(); // Keyed collection properties created in constructor.
using (IDbConnection connection = new SqlConnection("DB connection string here..."))
{
connection.Open();
// Dapper adds a generic method, Query<>, to the IDbConnection interface.
// Query<(1)ServiceCall, (2)Technician, (3)Customer, (4)ServiceCall> means
// construct a (1)ServiceCall, (2)Technician, and (3)Customer class per row, add to an IEnumerable<(4)ServiceCall> collection, and return the collection.
// Query<TFirst, TSecond, TThird, TReturn> expects SQL columns to appear in the same order as the generic types.
// It maps columns to the first class, once it finds a column named "Id" it maps to the second class, etc.
// To split on a column other than "Id", specify a splitOn parameter.
// To split for more than two classes, specify a comma-delimited splitOn parameter.
response.ServiceCalls.AddRange(connection.Query<ServiceCall, Technician, Customer, ServiceCall>(query, (ServiceCall, Technician, Customer) =>
{
// Notice Dapper creates many objects that will be discarded immediately (Technician & Customer parameters to lambda expression).
// The lambda expression sets references to existing objects, so the Dapper-constructed objects will be garbage-collected.
// So this is the cost of using Dapper. We trade unnecessary object construction for simpler code (compared to constructing objects from IDataReader).
// Each row in query results represents a single service call.
// However, rows repeat technician and customer data through joined tables.
// Avoid constructing duplicate technician and customer classes.
// Refer to existing objects in global collections, or add Dapper-mapped objects to global collections.
// This avoid creating duplicate objects to represent same data.
// Newtonsoft JSON serializer preserves object instances from service to client.
Technician technician;
Customer customer;
if (response.Technicians.Contains(Technician.Id))
{
technician = response.Technicians[Technician.Id];
}
else
{
response.Technicians.Add(Technician);
technician = Technician;
}
if (response.Customers.Contains(Customer.Number))
{
customer = response.Customers[Customer.Number];
}
else
{
response.Customers.Add(Customer);
customer = Customer;
}
// Set object associations.
ServiceCall.Technician = technician;
ServiceCall.Customer = customer;
technician.ServiceCalls.Add(ServiceCall);
if (!technician.Customers.Contains(customer))
{
technician.Customers.Add(customer);
}
customer.ServiceCalls.Add(ServiceCall);
if (!customer.Technicians.Contains(technician))
{
customer.Technicians.Add(technician);
}
return ServiceCall;
}, queryParameters, splitOn: splitOn));
}
return response;
}
Using this technique requires you to set PreserveReferencesHandling = true on the JsonSerializer class so object references are preserved on the client-side. Otherwise, Json.NET will construct duplicate objects and technician.Customers.Count will always == 1.
For example, if John Doe is assigned a service call at Acme and another at Contoso, his technician.Customers.Count will equal 1 if you leave PreserveReferencesHandling == false (Json.NET will construct two Technician objects each named John Doe).

Check if a list contains a string. Contains asks for a class object [duplicate]

This question already has answers here:
Using .Contains() on a property in a list
(8 answers)
Closed 6 years ago.
I'm reading a json file to a list using the following code:
string json = File.ReadAllText("rep.json");
var repList = JsonConvert.DeserializeObject<List<rep>>(json);
public class rep
{
public string userid;
public int repValue;
}
Now I'm trying to check if the replist contains a specific userid using the following code:
if (repList.Contains(user.AvatarId.ToString()))
{
}
But this contains method accepts a object from the rep class. How could I check this since i don't know the users rep values?
Screenshot:
Instead for .Contains you have to make a try with .Any() Which will returns a boolean value which represents the presence of any matching items. That is the condition will be true if any rep in repList with userid as user.AvatarId
if (repList.Any(x=> x.userid == user.AvatarId.ToString()))
{
// code your logic here
}

Assinging a sequential "row number" in linq output [duplicate]

This question already has answers here:
LINQ: Add RowNumber Column
(9 answers)
Closed 6 years ago.
I would like to access the row number in linq query.
There are many articles on the web stating how to do this but there is a catch:
I want to resolve the enumeration "later" and I want it to assign the same ID every time.
For this reason methods such as this do not work:
public IEnumerable<MyClass> GetThings(List<object> lst)
{
int ID=0;
return from i in lst
select new MyClass(ID++, i);
}
public class MyClass
{
public MyClass(int ID, object Stuff)
{ ... }
}
...
var x = GetThings(SomeList);
(fails because each time you resolve x by iterating each item gets a different id)
Actually you can use the Select overload that pass the index too, something like this:
lst.Select((o, i) => new MyClass(i, o));
Turns out the solution to this is quite simple - grab the row number of the source collection, not the output collection.
Obviously this only works if you are not filtering etc. and the number of items in your output collection is the same as the input collection. Unless you're ok with gaps and/or duplicates
i.e.
public IEnumerable<MyClass> GetThings(List<object> lst)
{
int ID=0;
return from i in lst.Select((item,id)=>new {Item=item, ID=id})
select new MyClass(i.ID, i.Item);
}
public class MyClass
{
public MyClass(int ID, object Stuff)
{ ... }
}
...
var x = GetThings(SomeList);
now the ID's of each item in x is the same every time you iterate it

AutoMapper - Why it is overwriting whole object? [duplicate]

This question already has answers here:
Automapper: Update property values without creating a new object
(4 answers)
Closed 4 years ago.
I don't understand why it's overwriting my whole object. The reason is that I get my User object from db an I want to assign new values from DTO. Instead of just adding those new values it's creating new object that has new values but all previous are set to null.
How can I make sure that in this case he will "upgrade" my object, not create new one?
Scenario
/users/{id} - PUT
// User has id, username, fullname
// UserPut has fullname
public HttpResponseMessage Put(int id, UserPut userPut)
{
var user = _db.Users.SingleOrDefault(x => x.Id == id); // filled with properties
Mapper.CreateMap<UserPut, User>();
user = Mapper.Map<User>(userPut); // now it has only "fullname", everything else set to null
// I can't save it to db because everything is set to null except "fullname"
return Request.CreateResponse(HttpStatusCode.OK, user);
}
The Mapper.Map has an overload which takes a source and a destination object. In this case Automapper will use the given destination object and does not create a new object.
So you need to rewrite your Mapper.Map to:
Mapper.Map<UserPut, User>(userPut, user);

What is the logic here with LINQ C# query? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Check the following program below from the book "Essential LINQ":
class Customer
{
public string CustomerID {get;set;}
public string ContactName {get;set;}
public string City {get;set;}
public static List<Customer> GetCustomers()
{
return new List<Customer>
{
new Customer {CustomerID = "ALFKI", ContactName = "Maria Anders", City = "Berlin"},
new Customer {CustomerID = "ANATR", ContactName = "Sns Trujillo", City = "Mexico D.F."},
new Customer {CustomerID = "ANTON", ContactName = "Antonio Moreno", City = "Mexico D.F."}
};
}
}
void Main()
{
var query = from c in Customer.GetCustomers()
where c.City == "Mexico D.F."
select new { c.City, c.ContactName};
foreach (var cityandcontact in query)
{
Console.WriteLine(cityandcontact);
}
}
In the LINQ Query, why it gives me error if I remove "new" from line:
select new {c.City, c.ContactName};
why we can't just write like this
select c.City, c.ContactName;
what is the logic here?
Queries return enumerations of objects of the same class. It can be any class, but it needs to be just one for all rows. You cannot return pairs of objects (a city and a contact name) without combining them into a single object. In this case, you are combining them into an object of anonymous type, with two properties.
On a side note, there is no advantage to using Select method at all, because both properties come from the same Customer class. In fact, there is less efficient than returning customer objects unchanged, like this:
var query = from c in Customer.GetCustomers()
where c.City == "Mexico D.F.";
foreach (var c in query)
{
Console.WriteLine("{0} {1}", c.City, c.ContactName);
}
LINQ methods, like other C# methods, can only return one value/object. (There is no syntax support added for said hypothetical construct to magically do what the new { .. } stuff does.)
This is why the different fields are "wrapped" in an new object: it is then a single value/object with members representing all the selected fields.
(Likewise, the final result, an IEnumerable<..>, is also just one value/object with 0 or more elements in the sequence: in this case each element is a new object created with new { .. } above.)
new { c.City, c.ContactName}; is the C# syntax for an anonymous type.
The 'select' selects a Customer class, which consists of a City variable and a ContactNamr variable. So, you have to provide select with a new class with those variables
This is not SQL this is linq, and the syntax is basically what you see.
select new { c.City, c.ContactName};
limits the output of the query to a type that contains just the City and the ContactName.
You may ask what type is this? Well it's an anonymous type that the compiler creates for you.
The code then iterates over the results of the query (IEnumerable<this_new_type>) and writes the ToString() of this new type.
select new is creating a new anonymous object.
"Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first"
select new {c.City, c.ContactName};
You could also create a concrete class to project into.
i.e a class called Blah:
class Blah
{
public string City {get;set;}
public string ContactName {get;set;}
}
... [rest of query]
select new Blah {c.City, c.ContactName};
The reason you cannot go "select c.City, c.ContactName;", is because the language syntax is not designed to handle it.

Categories