How to select the content of different table in one model - c#

What I have:
2 tables, called Content and AboutModel. In Content, I have the following design:
And the AboutModel has the folowing design:
I have these classes in my code:
public class Content : BaseEntity
{
public int Id { get; set; }
public string WebsiteTitle { get; set; }
public string WebsiteDescription { get; set; }
public virtual ICollection<AboutModel> About { get; set; }
}
public class ContentConfiguration : IEntityTypeConfiguration<Content>
{
public void Configure(EntityTypeBuilder<Content> b)
{
b.HasKey(x => x.Id);
}
}
public class AboutModel : BaseEntity
{
public int Id { get; set; }
public string Title { get; set; }
public string Message { get; set; }
public int ContentId { get; set; }
public virtual Content Content { get; set; }
}
public class AboutModelConfiguration : IEntityTypeConfiguration<AboutModel>
{
public void Configure(EntityTypeBuilder<AboutModel> b)
{
b.HasKey(x => x.Id);
}
}
What I had before adding foreign key was this:
var content = _contentRepository.Query().SingleOrDefault();
But when I take the row from the content table, the About collection is null. I need help to understand how to access the about contents.
My goal is to have a file.cshtml and have a model Content and access both Content and About contents from it and display it on the page.

there are few methods to reach that. You can try:
var queryResult = myDbContexy.Content.Include(foo => foo.About).SingleOrDefault();
Or:
var anonymousResult = myDbContext.Content.Select(foo => new {Content = foo, About = foo.About}).ToList();
You can also use Join steatment from LINQ but I don't like it ;)
For more you can read about lazy loading in EF.

Related

How can i fix Cannot implicitly convert Generic.List< > to Generic.List< >

How can i fix Cannot implicitly convert System.Collections.Generic.List <RPHistory> to System.Collections.Generic.List <RPHistory> exception error.
I am trying to combine two Entities together to get a single list
RP Entity Class:
public class RP
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
}
RPHistory Entity Class :
public class RPHistory:
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
}
And I created this third class
RpWithHistory Class :
public class RpWithHistory {
public int ID;
public int RPID;
public string Name;
public int ProductID;
public List<RPHistory> History;
}
Linq Query
var RPs = await Context.RP.Where(b => b.ProductID == request.ID)
.Select(x=> new RpWithHistory {
ID = x.ID,
RPID = x.RPID,
Name = x.Name,
ProductID = x.ProductID,
History = Context.RPHistory
.Where(y=> y.RPID
== x.RPID)
.ToList()
}
).ToListAsync();
But i get this error,
>Cannot implicitly convert System.Collections.Generic.List <RPHistory> to
>System.Collections.Generic.List <RPHistory> exception error
Thanks!
I am not sure why you're doing that. Can I suggest this?
You do not need to go all that way of creating a class that joins the two. Just create a Navigation property on your RP that points to RPHistory Objects.
public class RP
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
public ICollection<RPHistory> HistoryList { get; set; } // Navigation Property
}
public class RPHistory:
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
[ForeignKey(nameof(RPID))] // Identify the Foreign Key from RP Class
public RP RP { get; set; } // Navigation back to RP
}
Then you can chain everything into a single list using LINQ:
var RPs = Context.RP.Where(rp => rp.ProductID == request.ID)
.Include(rp=>rp.RPHistory) // This includes RPHistory
.ToList();
You need to clone or create a new list.
Option 1: Use ConvertAll
List<RPHistory> pPHistoryCopy = rphWithHistory.RPHistory.ConvertAll(history => new RPHistory(rphWithHistory.RPHistory));
Option 2:
//Clone Extension
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
Use the clone extention

Group by for list in list

I have list of Issues : List<Issue> where Issue is class :
public class Issue : BaseEntity
{
private string m_KeyString;
[JsonProperty("expand")]
public string Expand { get; set; }
[JsonProperty("id")]
public int Id { get; set; }
#region Special key solution
[JsonProperty("key")]
public string ProxyKey
{
get
{
return Key.ToString();
}
set
{
m_KeyString = value;
}
}
[JsonIgnore]
public IssueKey Key
{
get
{
return IssueKey.Parse(m_KeyString);
}
}
#endregion Special key solution
[JsonProperty("fields")]
public Fields Fields { get; set; }
}
Class Fields looks like this :
public class Fields
{
[JsonProperty("summary")]
public string Summary { get; set; }
[JsonProperty("assignee")]
public Assignee Assignee { get; set; }
[JsonProperty("worklog")]
public List<WorkLog> WorkLogs { get; set; }
}
And class WorkLog:
public class WorkLog : BaseEntity
{
[JsonProperty("updateAuthor")]
public string Author { get; set; }
[JsonProperty("timeSpent")]
public string TimeSpent { get; set; }
[JsonProperty("timeSpentSeconds")]
public int TimeSpentSeconds { get; set; }
}
I want to get on output Author - SUM (TimeSpentSeconds)
So i need to group by Author and then get SUM.
For each item in Issues list I make this :
var sumTime = issue.Fields.WorkLogs.GroupBy(x => x.Author).Select(x => new
{
User = x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
});
which group by user and count sum.
But how can i manage the same not just for one item in list but for all list?
OK, I think you're saying that you can run this query for a single Issue, but you want to be able to run it over all the items in a List<Issue>.
I'm assuming that you don't want to break it out separately by Issue, so what I'd do is this:
List<Issue> issues = // get Issues from wherever
var sumTime = issues.SelectMany(issue => issue.Fields.WorkLogs)
.GroupBy(x => x.Author)
.Select( x => new
{
x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
}
);
Disclaimer:
this was done entirely from memory, with no testing.

Prevent extra column in one to one relationship

I am using Entity-framework code first approach.
Table 1 looks like:
public class CreditUser : BaseEntity
{
public string Id { get; set; }
public string CreditUserName { get; set; }
public string Address { get; set; }
public string Description { get; set; }
public string EmailId { get; set; }
public Accounts Accounts { get; set; }
}
Table 2 looks like:
public class Accounts : BaseEntity
{
public string Id { get; set; }
public decimal TotalAmount { get; set; }
public decimal TotalGivenAmount { get; set; }
public CreditUser CreditUser { get; set; }
}
And fluent api code:
public class AccountsConfiguration : EntityTypeConfiguration<Accounts>
{
public AccountsConfiguration()
{
HasKey(t => t.Id);
HasRequired<CreditUser>(c => c.CreditUser)
.WithOptional(a => a.Accounts)
.WillCascadeOnDelete(false);
}
}
It produces two columns like,
That extra column in the end CreditUser_Id came as automatically. I don't want that. Please help me to solve that issue.
UPDATE
CreditUSer Table Table structure
Solution
As per comments below I tried like in this
public class AccountsConfiguration : EntityTypeConfiguration<Accounts>
{
public AccountsConfiguration()
{
HasRequired<CreditUser>(c => c.CreditUser)
.WithOptional(a => a.Accounts)
.WillCascadeOnDelete(false);
}
}
by removing Key in the fluent Api and it is working.

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);
}
}

A better way to retrieve EF data and collections

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 :)

Categories

Resources