ASP.Net MVC 5 Entity Framework Select Where In? - c#

I have 3 tables Project, Province, ProjProvRel
In my project page when I add data I select multiple provinces for each project
means my Province is multi select dropdown list.
I insert data it is working I get the inserted Id and added to ProjProvRel with selected Ids of Province
Now In details view I want to display my data but I could not solve it.
here is my code:
// GET: Project/Details/5
public ActionResult Details(int? id)
{
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var mydata = db.Projects.Find(id);
if (mydata == null)
{
return HttpNotFound();
}
var prov_id = from o in db.ProRel where o.ProjectId.Equals(id) select o.ProvinceIds;
foreach (var p_id in prov_id)
{
int[] PIds = new int[] {p_id};
}
var Prov= from c in db.Provinces where c.ID in pIds;
ViewBag.Province = Prov;
return View(mydata);
}
one problem is how can I select data from table based on where condition
var prov_id = from o in db.ProRel where o.ProjectId.Equals(id) select o.ProvinceIds;
is the above query correct ? I am new to ASP.Net MVC
also blow query is correct ?
var Prov= from c in db.Provinces where c.ID in pIds;
how can I select data from Table Province where province.ID in PIds

Get the rows from ProRel where the ProjectId matches with id:
var prov_ids = db.ProRel.Where(r => r.ProjectId == id).Select(r => r.ProvinceId);
Get the provinces that can be found from prov_id:
var provs = db.Provinces.Where(p => prov_id.Any(i => p.ID == i));
I hope you don't mind the lambda-style LINQ.
Also, I think you should consider your variable naming. For example, prov_id gives the idea of a single id and since you declared the type implicitly it is difficult to tell otherwise.

Related

How to make a List<> from 2 list of object

I have object CartItem and Product.
I need to make one List<> from these 2 objects.
Tried:
List<(CartItem,Product)> product = new List<(CartItem,Product)>();
Code:
public async Task<IActionResult> Index()
{
var username = User.Identity.Name;
if (username == null)
{
return Login();
}
var user = _context.Users.FirstOrDefault(x => x.Email == username);
List<CartItem> cart = _context.ShoppingCartItems.Where(s => s.IDuser.Equals(user.Id)).ToList();
List<Product> product = new List<Product>();
foreach (var item in cart)
{
product.Add(_context.Product.FirstOrDefault(x => x.id == item.ProductId));
}
return View(user);
}
The correct answer would be to not do that. What would be the expected result for 5 items and 2 products?
The code is loading both CartItem and Product objects from the database using loops. That's very slow as each object requires yet another query. This can be replaced with a single line producing a single SQL query.
If CartItem has Product and User properties (as it should) all the code can be replaced with :
var cart=_context.ShoppingCartItems
.Include(i=>i.Product)
.Where(s=>s.User.Email==userName)
.ToList();
EF Core will generate the SQL query that joins User, CartItem, Product together and returns items and their products, but no User data. The Product data will be available through the CartItem.Product property
What was asked but shouldn't be used
If a List<(CartItem,Product)> is absolutely required (why???) you could use that instead of a List<Product>, and add items inside the loop:
// PLEASE don't do it this way
var dontUseThis = new List<(CartItem,Product?)>();
foreach (var item in cart)
{
var product=_context.Product.FirstOrDefault(x => x.id == item.ProductId);
dontUseThis.Add((item,product));
}
This will result in one extra SQL query per cart item.
Slightly better
A slightly better option would be to generate a WHERE product.ID IN (....) clause, to load all relevant products in a single query. After that the two lists would have to be joined with JOIN.
var productIds=cart.Select(c=>c.ProductId);
var products=_context.Product
.Where(p=>productIds.Contains(p.Id))
.ToList();
var dontUseThis = products.Join(cart,
p => p.Id,
c => c.ProductId,
(c, p) => (c,p))
.ToList();
This will reduce the N+1 queries to 2. It's still slower than letting the database do the work though
First, see the answer of #Panagiotis Kanavos. Aside that, for combining part, you can do this:
List<CartItem> cartItems; // assuming you already have this
List<Product> products; // assuming you already have this
// The combination part
var result = from p in products
join ci in cartItems on p.Id = ci.ProductId // Find out how product and cartItem relates
select new (p,ci);
// Need List?
var resultingList = result.ToList();
You can make a struct and put both variables in there:
struct EntireProduct
{
CartItem cartItem;
Product product;
}
then you can create a list of that struct:
List<EntireProduct> product = new List<EntireProduct>();

Search Select not returning NotFound method

I wrote a simple select to find searched data in a database. Thing is, in my case when there is no item in the database, method is returning an empty json.
[Route("api/Atributes/{value}")]
public IHttpActionResult GetAtributeByValue(string value)
{
var atribute = ( from a in db.Atributes
join p in db.Cards on a.Atr_Nr equals p.Card_Nr
where a.Atr_Value == value
select new Employee
{
Name = p.Name,
Surname = p.Surname,
Number = a.Atr_Value
});
//this is statement id not working
if (atribute == null)
{
return NotFound();
}
return Ok(atribute);
}
Question is: Is this method of searching correct? If not how should I make it in another way?
Convert your result set to List and use its length property to check. LINQ has deferred execution. Fetch the actual result set with .ToList().
[Route("api/Atributes/{value}")]
public IHttpActionResult GetAtributeByValue(string value)
{
var atribute = ( from a in db.Atributes
join p in db.Cards on a.Atr_Nr equals p.Card_Nr
where a.Atr_Value == value
select new Employee
{
Name = p.Name,
Surname = p.Surname,
Number = a.Atr_Value
}).ToList();
//use count, if that does not work, length here
if (atribute.Count() == 0)
{
return NotFound();
}
return Ok(atribute);
}
Seem like that the method is trying to a list of Atribute. So you just return atribute directly
[Route("api/Atributes/{value}")]
public IHttpActionResult GetAtributeByValue(string value)
{
var atribute = ( from a in db.Atributes
join p in db.Cards on a.Atr_Nr equals p.Card_Nr
where a.Atr_Value == value
select new Employee
{
Name = p.Name,
Surname = p.Surname,
Number = a.Atr_Value
});
return Ok(atribute);
}
EDIT:
Why not 404 (Not Found) ?
The 404 status code should be reserved for situations, in which a resource is not found. In this case, your resource is a collection of Atribute. This collection exists but it's currently empty. It is very confused as an author of a client for your application if I got a 200 one day and a 404 the next day just because someone happened to remove a couple of Atributes. What am I supposed to do? Is my URL wrong? Did someone change the API and neglect to leave a redirection.

How do I order by a function in LINQ query?

I am developing my first MVC application with Entity Framework. I have a table USERS and a table RESTRICTIONS
In my controller I wrote a function that returns the number of common restrictions between two users:
public int common_restrictions(int id1, int id2)
{
MyModel bd = new MyModel();
int count= 0;
var restrictions = from c in bd.RESTRICTIONS where c.ID_USER == id1
select c;
var restrictions2 = from c in bd.RESTRICTIONS where c.ID_USER == id2
select c;
foreach (var prop in restrictions)
{
var nr = restrictions2.Count(p => p.ID_PROP == prop.ID_PROP);
if (nr != 0)
{
count++;
}
}
return count;
}
The function works as it supposed to.
Now in another function in the same controller I want to sort the list of users in descending order of their common restrictions with a specific user (let's say user with the id 12). I got the list of users in a query but I don't know how to sort them after that
var query = from u in bd.USERS where u.ID != 12
select u;
// sort the list??
I've tried
var query = from u in bd.USERS orderby(common_restrictions(u.ID,
12)) select u;
but I get the following error message:
"LINQ to Entities does not recognize the method 'Int32 common_restrictions (Int32, Int32)' method, and this method cannot be translated into a store expression."
You need to do this in two steps if you don't want to include another property to your User object. And I think this following way is easier.
var query = (from u in bd.USERS where u.ID != 12
select u).ToList();
var unSorteUsers = (from u in query
select new
{
User = u,
CR = common_restrictions(u.ID,12)
});
var sortedUsers = (from u in unSorteUsers
orderby u.CR
select new User
{
ID = u.User.ID,
//All other properties.
});

Using LINQ and EF, how to remove values from database where not in list of items

I have an existing set of items in a database where a MultiSelectList of Id's allows Shifts to be added to an Operator.
Currently, an Operator has 3 Shifts assigned. This is now edited on the View to remove 1 Shift from the 3.
What is the best way to go through the existing set of items and determine which of the 3 to remove that isn't found in the list of selected items.
I know the easiest method would be to remove all items in the database for the Operator and then insert the values from the MultiSelectList but thought there might be a way to use LINQ to select from existing where not in the list of Id's
var existing = (from os in db.OperatorShifts
where os.OperatorId == model.Id
select os);
public List<Guid?> Shifts { get; set; }
public MultiSelectList AvailableShifts { get; set; }
model.Shifts
//asuming your db has these fields: db.Shifts, db.OperatorShifts
//and model is the current Operator
var existingShifts = (from os in db.OperatorShifts
where os.OperatorId == model.Id
select os).ToList();
IEnumerable<Guid> newShiftIds = ?; //don't now how your selected shift ids got post back, figure it out yourself
var shiftsToRemove = existingShifts.Where(e => newShiftIds.All(id => e.Id != id)).ToList();
var shiftIdsToAppend = newShiftIds.Where(id => exising.All(e => e.Id != id)).ToList();
foreach(var shift in shiftsToRemove)
{
db.OperatorShifts.Remove(shift);
}
foreach(var shiftId in shiftIdsToAppend)
{
db.OperatorShifts.Add(new OperatorShift{
OperatorId = model.Id,
ShiftId = shiftId
});
}
I have found the simplest way to do tasks like this is to delete all the items from the database and then insert the ones I want back in.

Group two columns into one and return

I have a controller action which returns StudentID.
[HttpPost()]
public ActionResult DisplayStudents()
{
StudentDataContext db = new StudentDataContext ();
var StudentID = (from s in db.vwStudents.Where(s => s.StudentID != null)
group s by s.StudentID into g
select g.Key).ToList();
return Json(new {StudentID});
}
I want to return "StudentName" from the same view as well, but parse the final result and return "StudentID - StudentName" for e.g. "JD11212 - John Deat"
Is there a way where I can group two columns "StudentID" and "StudentName" in one and return that?
...
group s by new {s.StudentID, s.StudentName} into g
...
This will create a new anonymous type {StudentID, StudentName} which should be what you are looking for.

Categories