A better way to retrieve EF data and collections - c#

So I have a model that contains a list of models which contains items, and so on, like this:
public partial class CART
{
public CART()
{
//this.CART_DETAIL = new HashSet<CART_DETAIL>();
this.CART_DETAIL = new List<CART_DETAIL>();
}
public int CART_IDE { get; set; }
public int CART_COUNT { get; set; }
public string SHOPPING_CART_IDE { get; set; }
public virtual IList<CART_DETAIL> CART_DETAIL { get; set; }
}
public partial class CART_DETAIL
{
public int CART_DETAIL_IDE { get; set; }
public int CART_IDE { get; set; }
public int CART_DETAIL_COUNT { get; set; }
public Nullable<int> PACK_IDE { get; set; }
public Nullable<int> BACKSTORE_INVENTORY_IDE { get; set; }
public virtual CART CART { get; set; }
public virtual PACK PACK { get; set; }
public virtual BACKSTORE_INVENTORY BACKSTORE_INVENTORY { get; set; }
}
public partial class BACKSTORE_INVENTORY
{
public BACKSTORE_INVENTORY()
{
this.CART_DETAIL = new HashSet<CART_DETAIL>();
this.ORDER_DETAIL = new HashSet<ORDER_DETAIL>();
}
public int BACKSTORE_INVENTORY_IDE { get; set; }
public int INVENT_IDE { get; set; }
public int STORE_IDE { get; set; }
public decimal BACKSTORE_INVENTORY_PRICE { get; set; }
public int BACKSTORE_STOCK_QTY { get; set; }
public decimal BACKSTORE_DISCOUNT { get; set; }
public decimal BACKSTORE_SELLING_PRICE { get; set; }
public virtual INVENTORY INVENTORY { get; set; }
public virtual STORE STORE { get; set; }
public virtual ICollection<CART_DETAIL> CART_DETAIL { get; set; }
public virtual ICollection<ORDER_DETAIL> ORDER_DETAIL { get; set; }
}
When I open a connection and consult the data, everything's fine, but if I retrive the whole data in a view, for example, unless I modify the Hashset to a List and then proceed like this:
CART cart =
db.CART.FirstOrDefault(_item => _item.SHOPPING_CART_IDE == mShoppingCartID && _item.CART_ACTIVE_INDICATOR);
if (cart != null)
{
cart.CART_EXP_TIME = DateTime.Now.AddMinutes(90);
cart.USER_SESSION_IDE = UserSessionManager.GetUserSession().mUserSessionID;
cart.CART_DETAIL = cart.CART_DETAIL.ToList();
foreach (var cartDetail in cart.CART_DETAIL)
{
if(cartDetail.BACKSTORE_INVENTORY_IDE != null)
{
cartDetail.BACKSTORE_INVENTORY =
db.BACKSTORE_INVENTORY.First(_item => _item.BACKSTORE_INVENTORY_IDE == cartDetail.BACKSTORE_INVENTORY_IDE);
cartDetail.BACKSTORE_INVENTORY.INVENTORY =
db.INVENTORY.Find(cartDetail.BACKSTORE_INVENTORY.INVENT_IDE);
cartDetail.BACKSTORE_INVENTORY.INVENTORY.CARD =
db.CARD.Find(cartDetail.BACKSTORE_INVENTORY.INVENTORY.CARD_IDE);
}
else
{
cartDetail.PACK = db.PACK.First(_item => _item.PACK_IDE == cartDetail.PACK_IDE);
}
}
db.SaveChanges();
}
I get the following error: CS0021: Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.ICollection<MyApp.Models.DAL.Entities.CART_DETAIL>' which I understand is because the ICollection does not afford indexing, and then I get The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. for items that I forgot to retrive.
So my question: what makes this happen? Is there a way to retrieve all the data at once without having to get all specific items separately? A better way to do things?

What are you trying to achieve form the above code?
I am struggling to follow what your end goal is but would something along these lines be what you are looking for:
public List<Cart> GetAllInCart()
{
return db.CART.Where(a => a.Cart_IDE == CartIDE)
.Include(x => x.Cart_Detail)
.Include(x => x.Cart_Detail.Pack)
.Include(x => x.Cart_Detail.Backstore_Inventory)
.ToList()
}
I hope this helps :)

Related

Entity Framework to List without specific Column

I'm actually using EF and very new to it. I have a EDMX-Model from Database. I have to get a List of Entities, but in this Table is Binary-Column. I want my Object-List from that Table without the Binary Data.
My Entity-Objekt looks like:
public partial class bons
{
public int id { get; set; }
public System.DateTime zeitstempel { get; set; }
public byte[] bonBinaer { get; set; }
public Nullable<int> tisch_nr { get; set; }
public bool abgearbeitet { get; set; }
public int kunde_id { get; set; }
public string hash { get; set; }
public string dateiname { get; set; }
public string tisch_name { get; set; }
public int gang { get; set; }
public decimal brutto { get; set; }
public Nullable<System.DateTime> zeitstempelAbgearbeitet { get; set; }
public Nullable<int> positionenAnzahl { get; set; }
public bool manuell { get; set; }
}
And I#m getting the List like:
internal static List<bons> holeBongListeNichtAbgearbeitetRestaurant(int kunde_id)
{
List<bons> rückgabe = new List<bons>();
using (bonsEntities context = new bonsEntities())
{
rückgabe = context.bons.Where(x => x.kunde_id == kunde_id && x.abgearbeitet == false).OrderBy(x => x.zeitstempel).ToList();
}
return rückgabe;
}
Can someone help how to get the List without the 'byte[] bonBinaer'?
Hi you can use autoMapper with EF extension
like: https://docs.automapper.org/en/stable/Queryable-Extensions.html
or if you prefer you can do by yourself with a Select() method like:
using (bonsEntities context = new bonsEntities())
{
rückgabe = context.bons.Where(x => x.kunde_id == kunde_id && x.abgearbeitet == false).OrderBy(x => x.zeitstempel).Select(xx=> new {
id = xx.id
//and all you props
}).ToList().Select(yy=> new bons{
id=yy.id
//and back with other props
}).ToList();
}

MVC4: Retrieving a related list with Entity and casting it as List<> or IEnum<> for View Model

This a simple project where users can search for job postings by area of expertise. The relationship between Areas and Postings are Many-to-many. I seem to be able to get to the very last part of retrieving the correctly filtered list, but getting back into the view model keeps giving me different errors:
ViewModel:
public class AreaOfertasViewModel
{
public Oferta UnaOferta { get; set; }
public SelectList AreasTrabajo { get; set; }
public IEnumerable<Oferta> Ofertas { get; set; }
public int idArea { get; set; }
public AreaOfertasViewModel()
{
this.UnaOferta = UnaOferta;
this.Ofertas = new List<Oferta>();
cargarAreas();
}
private void cargarAreas()
{
PostulaOfertaContext db = new PostulaOfertaContext();
this.AreasTrabajo = new SelectList(db.Areas, "areaId", "Area");
}
}
}
Controller:
public ActionResult SearchXArea()
{
return View(new AreaOfertasViewModel());
}
[HttpPost]
public ActionResult SearchXArea(AreaOfertasViewModel aovm)
{
int id = aovm.idArea;
PostulaOfertaContext db = new PostulaOfertaContext();
var area = db.Areas.Where(c => c.areaId == id);
var ofertas = from c in db.Ofertas.Where(r => r.AreaTrabajo == area)
select c;
aovm.Ofertas = (IEnumerable<Oferta>)ofertas.ToList();
return View(aovm);
}
The line giving me issues is
aovm.Ofertas = (IEnumerable)ofertas.ToList();
I've tried List<> for Ofertas, and I've tried leaving it as .ToList() without casting, and casting it as different things, but it gives me errors about not being able to cast it, and "Cannot compare elements of type 'System.Collections.Generic.List`1'. Only primitive types, enumeration types and entity types are supported."
What's the solution here?
Model for AreaTrabajo:
public class AreaTrabajo
{
[Key]
public int areaId { get; set; }
public string Area { get; set; }
public virtual List<Oferta> oferta { get; set; }
}
Model for Oferta:
public class Oferta
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Titulo { get; set; }
[Required]
public DateTime Vencimiento { get; set; }
[Required]
public string Cargo { get; set; }
[Required]
public int HorarioComienzo { get; set; }
[Required]
public int HorarioFin { get; set; }
[Required]
public string DescripcionTareas { get; set; }
public int Remuneracion { get; set; }
[Required]
public int RangoEdadMin { get; set; }
[Required]
public int RangoEdadMax { get; set; }
public string TipoFormacion { get; set; }
public string Idiomas { get; set; }
public string Competencias { get; set; }
public string OtrosEstudios { get; set; }
public string Estado { get; set; }
public virtual List<AreaTrabajo> AreaTrabajo { get; set; }
public virtual TipoContrato TipoContrato { get; set; }
public virtual Empresa Empresa { get; set; }
public virtual List<Postulante> Postulantes { get; set; }
}
Answer
[HttpPost]
public ActionResult SearchXArea(AreaOfertasViewModel aovm)
{
int id = aovm.idArea;
PostulaOfertaContext db = new PostulaOfertaContext();
var area = db.Areas.Where(c => c.areaId == id).FirstOrDefault();
var ofertas = db.Ofertas.Where(s => s.AreaTrabajo.All(e => e.areaId == area.areaId)).ToList();
aovm.Ofertas = ofertas;
return View(aovm);
}
Sorry if my question wasn't clear enough. I needed to filter out from the many-to-many relationship, and this solved it.
You are getting an error because the actual sql is executed when you call tolist(). The error is in your sql because you are comparing AreaTrabago to a list.
[HttpPost]
public ActionResult SearchXArea(AreaOfertasViewModel aovm)
{
int id = aovm.idArea;
PostulaOfertaContext db = new PostulaOfertaContext();
var area = db.Areas.Where(c => c.areaId == id).FirstOrDefault();
var ofertas = db.Ofertas.Where(s => s.AreaTrabajo.All(e => e.areaId == area.areaId)).ToList();
aovm.Ofertas = ofertas;
return View(aovm);
}
Sorry if my question wasn't clear enough. I couldn't get the many-to-many relationship, and this solved the filtering problem perfectly.

Collections duplicated when trying to update a detached entity's related collection

I have two API calls. GetExam and SaveExam. GetExam serializes to JSON which means by the time I go to save, the entity is detached. This isnt a problem, I can go retrieve the entity by its primary key and update its properties manually.
However, when I do so the exam questions get its current collection duplicated. For example, if examToSave.ExamQuestions had a few questions deleted, and a new one added all selectedExam.exam_question are duplicated and the new one is added in. Eg. if 3 questions existed, I deleted 1 and added 4 there will now be 7.
Domain models:
public partial class exam
{
public exam()
{
this.exam_question = new HashSet<exam_question>();
}
public int ID { get; set; }
public string ExamName { get; set; }
public string ExamDesc { get; set; }
public Nullable<decimal> TimeToComplete { get; set; }
public bool AllowBackStep { get; set; }
public bool RandomizeAnswerOrder { get; set; }
public int Attempts { get; set; }
public virtual ICollection<exam_question> exam_question { get; set; }
}
public partial class exam_question
{
public exam_question()
{
this.exam_answer = new HashSet<exam_answer>();
}
public int ID { get; set; }
public int ExamID { get; set; }
public string QuestionText { get; set; }
public bool IsFreeForm { get; set; }
public virtual exam exam { get; set; }
public virtual ICollection<exam_answer> exam_answer { get; set; }
}
public partial class exam_answer
{
public int ID { get; set; }
public string AnswerText { get; set; }
public int QuestionID { get; set; }
public bool IsCorrect { get; set; }
public virtual exam_question exam_question { get; set; }
}
Save method:
[Route("SaveExam")]
[HttpPost]
public IHttpActionResult SaveExam(ExamViewModel examToSave)
{
using (var db = new IntranetEntities())
{
// try to locate the desired exam to update
var selectedExam = db.exams.Where(w => w.ID == examToSave.ID).SingleOrDefault();
if (selectedExam == null)
{
return NotFound();
}
// Redacted business logic
// Map the viewmodel to the domain model
Mapper.CreateMap<ExamAnswerViewModel, exam_answer>();
Mapper.CreateMap<ExamQuestionViewModel, exam_question>().ForMember(dest => dest.exam_answer, opt => opt.MapFrom(src => src.QuestionAnswers));
Mapper.CreateMap<ExamViewModel, exam>().ForMember(dest => dest.exam_question, opt => opt.MapFrom(src => src.ExamQuestions));
var viewmodel = Mapper.Map<exam>(examToSave);
// Update exam properties
selectedExam.ExamName = viewmodel.ExamName;
selectedExam.ExamDesc = viewmodel.ExamDesc;
selectedExam.AllowBackStep = viewmodel.AllowBackStep;
selectedExam.Attempts = viewmodel.Attempts;
selectedExam.RandomizeAnswerOrder = viewmodel.RandomizeAnswerOrder;
selectedExam.exam_question = viewmodel.exam_question; // DUPLICATES PROPS
// Save
db.SaveChanges();
return Ok(examToSave);
}
}

Find object with in class using LINQ

I want to return the item that has the profile ID I send. So in order to do this I will need to loop through all of the Items -> WebProproperties -> profile. The Class structure is at the end of the question.
I would rather use LINQ than create a nested foreach. I have been trying to get this to work for more than an hour now. I am stuck.
My first idea was to simply use where. But that doesn't work because you need to have something on the other side that needs to equal.
this.Accounts.items.Where(a => a.webProperties.Where(b => b.profiles.Where(c => c.id == pSearchString)) ).FirstOrDefault();
My second idea was to try using Exists which I don't have much experience with:
Item test = from item in this.Accounts.items.Exists(a => a.webProperties.Exists(b => b.profiles.Exists(c => c.id == pSearchString))) select item;
This doesn't work either:
Could not find an implementation of query pattern for source type 'Bool'
public RootObject Accounts {get; set;}
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
public List<Item> items { get; set; }
}
public class Profile
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
public class WebProperty
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string internalWebPropertyId { get; set; }
public string level { get; set; }
public string websiteUrl { get; set; }
public List<Profile> profiles { get; set; }
}
public class Item
{
public string id { get; set; }
public string kind { get; set; }
public string name { get; set; }
public List<WebProperty> webProperties { get; set; }
}
You can use Any() to determine existence. Also, note that many of the extension methods have overloads which take a predicate, including FirstOrDefault():
this.Accounts.items.FirstOrDefault(a => a.webProperties
.Any(b => b.profiles
.Any(c => c.id == pSearchString)));
You are looking for the .Any() operation I think. This will return true/false for whether there are any items matching your query.
For example:
if (this.Accounts.Items.Any(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)));
EDIT: You have full answer (was posted while I was composing mine) and as pointed out in comments my answer isn't actually returning your found item, just letting you know whether there is one. You can rework the first .Any to be a .FirstOrDefault to get that match.
E.g.
var result = this.Accounts.Items.FirstOrDefault(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)))
You can use the below mentioned code.
var abc = rr.items.Where(p => p.webProperties.Any(c => c.profiles.Any(d => d.id == "1"))).FirstOrDefault();
Just for your reference, your class should look like:
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
private List<Item> _items=new List<Item>();
public List<Item> items
{
get { return _items; }
set { _items = value; }
}
}

Updating List<T> in DbContext

I have a Model like this
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<string> SolvedBy { get; set; }
}
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
}
and then Controller like this. But I cannot update the List "SolvedBy", the next time I step through with the debugger, the list is still empty.
[HttpPost]
public string Index(string flag = "", int id=0)
{
Challenge challenge = db.Challenges.Find(id);
if (flag == challenge.Flag)
{
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<string>();
}
chall.SolvedBy.Add(User.Identity.Name);
db.Entry(chall).State = EntityState.Modified;
db.SaveChanges();
//congrats, you solved the puzzle
return "got it";
}
else
{
return "fail";
}
}
is there any way around it to make a list of strings kept in the database?
EF don't know how to store an array in database table so it just ignore it. You can create another table/entity or use XML/JSON to store the list. You can serialize the list before saving and deserialize it after loading from database
A List<T> in a model would normally map to a second table, but in your DbContext you only have a single table. Try adding a second table.
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
public DbSet<Solution> Solutions {get; set;}
}
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<Solution> SolvedBy { get; set; }
}
public class Solution
{
public int ID { get; set; }
public string Name { get; set; }
}
Then your controller can use code along the lines of...
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<Solution>();
}
chall.SolvedBy.Add(new Solution {Name=User.Identity.Name});
None of the above has been tested and I may have made some mistakes there, but the general principle I want to illustrate is the fact that you need another table. The List<T> represents a JOIN in SQL.

Categories

Resources