View complex Many to Many relationship on ADO.net - c#

I'm trying to update related database on many to many relationship using ADO.net
this is my database design:
as you guys notice, entity framework wont mapping the class_student & subject_course, i've been searching the method and found this website: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
the website told me to make a viewModel, and i do so:
namespace Test.Models.ViewModels
{
public class AssignedStudentData
{
public int ID { get; set; }
public string course_code { get; set; }
public bool Assigned { get; set; }
}
}
It's work flawlessly, but my problem is this line of code:
private void PopulateAssignedStudentData(ms_class ms_class)
{
var allStudent = db.ms_student; //this line is the problem
var ClassStudent = new HashSet<int>(ms_class.ms_student.Select(c => c.ID));
var viewModel = new List<AssignedStudentData>();
foreach (var student in allStudent)
{
viewModel.Add(new AssignedStudentData
{
ID = student.ID,
course_code = student.ms_course.course_name,
Assigned = ClassStudent.Contains(student.ID)
});
}
ViewBag.Students = viewModel;
}
in var allStudent, i've tried to make so the system not generate all the student, but instead, student THAT ASSIGNED WITH A SUBJECT so for example:
private void PopulateAssignedStudentDataBySubject(ms_class ms_class, int subject_id)
{
//var allStudent = db.ms_student; //this line is the problem
//My Version:
var allStudentByCourse = db.ms_student.Include(m => m.ms_course).Where(m => m.ms_course.ms_subject.subject_id == subject_id); //this code is not working
var ClassStudent = new HashSet<int>(ms_class.ms_student.Select(c => c.ID));
var viewModel = new List<AssignedStudentData>();
foreach (var student in allStudentByCourse )
{
viewModel.Add(new AssignedStudentData
{
ID = student.ID,
course_code = student.ms_course.course_name,
Assigned = ClassStudent.Contains(student.ID)
});
}
ViewBag.Students = viewModel;
}
i think the code won't work because the ms_course and ms_subject is a many-to-many relationship..
Thank you very much
Class
public partial class ms_course
{
public ms_course()
{
this.ms_student = new HashSet<ms_student>();
this.ms_subject = new HashSet<ms_subject>();
}
public int course_id { get; set; }
public string course_code { get; set; }
public string course_name { get; set; }
public virtual ICollection<ms_student> ms_student { get; set; }
public virtual ICollection<ms_subject> ms_subject { get; set; }
}

I understand that you're looking for students having a course that has at least one specific subject assigned to it. That would be:
db.ms_student
.Where(s => s.ms_course.ms_subject
.Any(sb => sb.subject_id == subject_id)))
It always helps me to articulate the problem clearly in terms of the object model first, as I did in the first sentence. It usually reveals what the query should look like.

What does the error message say?
You can try tis:
var allStudentByCourse = db.ms_student.Include(m => m.ms_course).Include("ms_course.ms_subject").Where(m => m.ms_course.ms_subject.subject_id == subject_id);
alternativ2 (this only works if ms_course has a fk property to ms_subject):
var allStudentByCourse = db.ms_student.Include(m => m.ms_course).Where(m => m.ms_course.subject_id == subject_id);
Update:
var allStudentByCourse = db.ms_student.Include(m => m.ms_course).Include("ms_course.ms_subject").Where(m => m.ms_course.ms_subject.Any(s => s.subject_id == subject_id));

Related

Sitecore Lucene Search With GlassMapper not working

I have been trying to teach myself Sitecore for the past couple of weeks.
At the moment i am trying to create a list of Recipes for users to search through.
However every Recipe contains Ingredients, Lucene returned these Ingredients as strings containing Item ID's. I wanted to have a List of Ingredients in my code so i gave GlassMapper a shot.
So i excluded the Ingredient list in my code from Lucene by changing the name so Lucene couldn't find the field.
I then set-up GlassMapper to fill the Ingredient list. The list stays null however.
How do i make GlassMapper fill this list for me?
My code:
Recipe class
[SitecoreType(TemplateId= "{1CF86642-6EC5-4B26-B8A7-1B2EC41F7783}")]
public class Recipe : SearchResultItem
{
[SitecoreId]
public Guid Id { get { return base.ItemId.Guid; } }
public virtual string RecipeName { get; set; }
public virtual string BookName { get; set; }
public virtual IEnumerable<Ingredient> _Ingredients { get; set; }
public virtual int AmountOfPeople { get; set; }
}
Ingredient class
[SitecoreType(TemplateId = "{730A0D54-A697-4DAA-908A-279CD24A9F41}")]
public class Ingredient : SearchResultItem
{
[SitecoreId]
Guid Id { get; }
[IndexField("Name")]
public virtual string IngredientName { get; set; }
}
GlassMapperScCustom class (I've only edited this method)
public static IConfigurationLoader[] GlassLoaders()
{
var attributes = new SitecoreAttributeConfigurationLoader("Receptenboek");
var loader = new SitecoreFluentConfigurationLoader();
var config = loader.Add<Recipe>();
config.Id(x => x.ItemId);
config.Info(x => x.Language).InfoType(SitecoreInfoType.Language);
config.Info(x => x.Version).InfoType(SitecoreInfoType.Version);
config.Field(x => x._Ingredients);
config.Info(x => x.Uri).InfoType(SitecoreInfoType.Url);
return new IConfigurationLoader[] {attributes, loader };
}
Recipe Controller
[HttpGet]
public ActionResult Index()
{
List<Recipe> recipes;
IQueryable<Recipe> query;
string index = string.Format("sitecore_{0}_index", Sitecore.Context.Database.Name);
var sitecoreService = new SitecoreService(Sitecore.Context.Database.Name);
string search = WebUtil.GetQueryString("search");
using (var context = ContentSearchManager.GetIndex(index).CreateSearchContext())
{
if (!string.IsNullOrEmpty(search))
{
query = context.GetQueryable<Recipe>().Where(p => p.Path.Contains("/sitecore/Content/Home/Recipes/")).Where(p => p.TemplateName == "Recipe").Where(p => p.RecipeName.Contains(search));
}
else
{
search = "";
query = context.GetQueryable<Recipe>().Where(p => p.Path.Contains("/sitecore/Content/Home/Recipes/")).Where(p => p.TemplateName == "Recipe");
}
recipes = query.ToList();
foreach( var r in recipes)
{
sitecoreService.Map(r);
Sitecore.Diagnostics.Log.Audit("SWELF" + r.RecipeName + "- " + r.BookName + " - " + r.AmountOfPeople + " - " + r.Name + "--" + r._Ingredients.Count(), this);
}
}
RecipesViewModel bvm = new RecipesViewModel() { Recipes = recipes, Search = search };
return View(bvm);
}
After playing around for a while i decided to split my search and mapping a little more. I used my Recipe model with Lucene and created a ViewModel to map the fields to with GlassMapper.
The Recipe class did not change.
The Ingredient class did not change.
The GlassMapperScCustom class was not needed so i restored its default.
RecipeViewModel class
After mapping to this class the Ingredient list had the correct amount of ingredients however all its fields where null.
After looking around on the internet a little more i found this stackoverflow post: Why isn't my Enumerable getting populated by Glass.Mapper?
I decided to give the SitecoreFieldType a go and it did the trick!
[SitecoreType(TemplateId = "{1CF86642-6EC5-4B26-B8A7-1B2EC41F7783}", AutoMap = true)]
public class RecipeViewModel : BaseFields
{
[SitecoreId]
public ID Id { get; set; }
public virtual string RecipeName { get; set; }
public virtual string BookName { get; set; }
[SitecoreField(FieldId = "{D1603482-7CBC-4E55-9CCB-E51DC0FC5A0B}", FieldType = SitecoreFieldType.Multilist)]
public virtual IEnumerable<IngredientViewModel> Ingredients { get; set; }
public virtual int AmountOfPeople { get; set; }
}
Recipe Controller
It turned out to be mapping the wrong way aswel. I found an example of a list being mapped to another list using SitecoreService.GetItem<>()
[HttpGet]
public ActionResult Index()
{
List<RecipeViewModel> recipes;
List<Recipe> query;
string index = string.Format("sitecore_{0}_index", Sitecore.Context.Database.Name);
var sitecoreService = new SitecoreService(Sitecore.Context.Database.Name);
string search = WebUtil.GetQueryString("search");
//Search with Lucene
using (var context = ContentSearchManager.GetIndex(index).CreateSearchContext())
{
if (!string.IsNullOrEmpty(search))
{
query = context.GetQueryable<Recipe>().Where(p => p.Path.Contains("/sitecore/Content/Home/Recipes/")).Where(p => p.TemplateName == "Recipe").Where(p => p.RecipeName.Contains(search)).ToList();
}
else
{
search = "";
query = context.GetQueryable<Recipe>().Where(p => p.Path.Contains("/sitecore/Content/Home/Recipes/")).Where(p => p.TemplateName == "Recipe").ToList();
}
}
//Map to ViewModel
recipes = query.Select(x => sitecoreService.GetItem<RecipeViewModel>(x.ItemId.Guid)).ToList();
RecipesViewModel bvm = new RecipesViewModel() { Recipes = recipes, Search = search };
return View(bvm);
}
One more problem
Because my ViewModel does not inherit from SearchResultItem a lot of useful fields where lost in the mapping. To keep the fields I needed from the SearchResultItem I made a BaseFields class for my ViewModel to inherit. I only needed Url for now but this can be easily expanded with more fields.
public class BaseFields
{
public virtual string Url { get; set; }
}

Entity Framework 6.0 updating List of items creates new records in the Database

The following are the entity classes to make more understanding of relationships:
public class EmployeeCv : UserEntity
{
public byte ProfileImage { get; set; }
public virtual List<Header> Headers { get; set; }
public virtual List<ProjectExperience> ProjectExperiences { get; set; }
public virtual List<Tag> Tags { get; set; } //many to many relationship between employeeCv and tags
[NotMapped]
public List<TagsByTypes> TagsbyTypes
{
get
{
List<TagsByTypes> ListOfTagTypes = new List<TagsByTypes>();
if (Tags != null)
{
var GroupedList = Tags.GroupBy(x => x.TagType.Title).ToList().Select(grp => grp.ToList());
foreach (var currentItem in GroupedList)
{
var TagType = new TagsByTypes()
{
Title = currentItem.FirstOrDefault().TagType.Title,
Tags = currentItem
};
ListOfTagTypes.Add(TagType);
}
return ListOfTagTypes;
}
else
return null;
}
}
}
public class Tag : AuditableEntity<int>
{
public string Title { get; set; }
public virtual List<EmployeeCv> EmployeeCv { get; set; }
public virtual TagType TagType { get; set; }
//To post Id's Not added to the database
[NotMapped]
public int TagTypeId { get; set; }
[NotMapped]
public int EmployeeCv_Id { get; set; }
}
public class TagType : AuditableEntity<int>
{
public string Title { get; set; }
public virtual List<Tag> Tags { get; set; }
}
I am writing a function to add new tag to the employeeCv based on the existing tag type. I have got Unit of work and Repositories setup to add/update/delete records in DB. Here is my implementation:
public void UpdateEmployeeCVWithTag(Tag tag)
{
using (var repository = new UnitOfWork<EmployeeCv>().Repository)
{
var EmployeeCv = repository.GetSingleIncluding(tag.EmployeeCv_Id,
x => x.Headers, x => x.Tags,
x => x.ProjectExperiences,
x => x.ProjectExperiences.Select(p => p.AssociatedProject),
x => x.ProjectExperiences.Select(p => p.ProjectSkills));
//x => x.ProjectExperiences.Select(p => p.ProjectSkillTags.Select(s => s.AssociatedSkill)));
//tag.TagType = EmployeeCv;
var repositoryTagType = new UnitOfWork<TagType>().Repository;
var tagtype = repositoryTagType.GetItemById(tag.TagTypeId);
tag.TagType = tagtype; //even after assignment new tagtype is creating everytime code runs
//repositoryTag.UpdateItem(tagtype);
EmployeeCv.Tags.Add(tag);
//EmployeeCv.ProjectExperiences[projectId - 1].ProjectSkills.Add(tag);
repository.UpdateItem(EmployeeCv);
}
}
This function works correctly except one issue. It is creating a new TagType in the database and ignoring the one that already exist. Below is my updateItem code in the repository classs:
public virtual void UpdateItem(TEntity entityToUpdate)
{
var auditableEntity = entityToUpdate as IAuditableEntity;
if (auditableEntity != null)
{
auditableEntity.UpdatedDate = DateTime.Now;
}
//_context
//Attach(entityToUpdate);
_context.Entry(entityToUpdate).State = EntityState.Modified;
_context.SaveChanges();
}
My guess without seeing the full functionality, is that you are using different context for this.
You should update the foreign key not the entire object so there is no need to add the entire TagType object since the tagTypeId is already set. The foreign key should work as is.
Please look into this link for further information.

How to improve LINQ repository query in MVC 4

I'm new to MVC and I'm trying to write a method (CheckIfDeletePossible) that checks whether the given CurrencyID is being used in the ProjectCurrency table.
Following is my first attempt and the query appears to be very slow.
Is there any better way to check this without looping the project table?
Currency Controller:
private bool CheckIfDeletePossible(int currencyID)
{
var lIsUsed = false;
var projectCurr = projectRepository.All;
foreach (var projects in projectCurr){
var project = projectRepository.AllIncluding(p => p.ProjectCurrencies.Select(c => c.Currency))
.Where(x => x.ProjectID == projects.ProjectID)
.Single();
var projCurrency = from projCurr in project.ProjectCurrencies
where projCurr.Currency.CurrencyID == currencyID
select projCurr.Currency;
if (projCurrency.Count() > 0)
{
lIsUsed = true;
return lIsUsed;
}
}
return lIsUsed;
}
Project Model:
public partial class Project:
{
public Project()
{
ProjectCurrencies = new List<ProjectCurrency>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Display(Name="ID")]
public int ProjectID { get; set; }
[Required]
[Display(Name = "Project Title")]
public string Title { get; set; }
[Display(Name = "Currency Rates")]
public virtual List<ProjectCurrency> ProjectCurrencies { get; set; }
}
You have added unnecessary complexity to your method, and the foreach is not needed.
You can check if a currency is used in a project with a snippet of code as simple as:
using (var repo = new ProjectRepository())
{
var used = repo.AllIncluding(p=>p.ProjectCurrencies)
.Any(p => p.ProjectCurrencies.Any(pc => pc.Currency.CurrencyID == 2));
}

Linq "join" with a IList<T> getting "Error Unable to create a constant value.."

I have a Save Method that saves with a Linq query a manually re-orderd list (in a web form) that is passed as the parameter to my method, and I try to update the Order Property of the IEnumerable<VM_CategoryLabel> I retrieve from the database (EF) with the corresponding value in the list (maybe would that be clearer with my code below):
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
//Order = x.Sequence_Cat,
Order = (int)listTemplate.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
I used the "test" var to see if my "sub-query" gets the correct value, and it does, but when I use my Linq expression inside the Select (the commented Order line), I get the following error:
Unable to create a constant value of type 'Namespace.Models.VM_CategoryLabelExtra. "Only primitive types and enumeration types are supported in this context.
Here are my classes:
public class VM_CategoryLabel
{
public int Id { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
public class VM_CategoryLabelExtra
{
public int Id { get; set; }
public int IdCat { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
So I suppose that I should not query the list inside my query ? So how do I "match" the 2 lists of values ?
I also tried the following (after having replace in the Linq query: Order = x.Sequence_Cat)that is not working neither because the iteration variable is
read-only:
foreach (var item in requete)
{
item.Order = listTemplate.Where(x => x.Id == item.Id).Select(x => x.Order).FirstOrDefault();
}
try
{
context.SaveChanges();
I suggest using this.
It is the let clause.
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
let list = listTemplate
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
Order = list.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
edit: instead offrom you can just do let list = listTemplate
Should work now :)
example for let:
// The let keyword in query expressions comes in useful with subqueries: it lets
// you re-use the subquery in the projection:
from c in Customers
let highValuePurchases = c.Purchases.Where (p => p.Price > 1000)
where highValuePurchases.Any()
select new
{
c.Name,
highValuePurchases
}
If you do not know how Let working than please download LinqPad and see an example

LINQ Error with DTO

I'm seeing a weird issue and I think I'm missing something. The DTO doesn't seem to get populated when returned. Only the properties that are being init in the LINQ query get set, the ones that are being set in ParseJobResultsXml do not get setup as seen in the DEBUG output.
The DTO is being setup in a LINQ query, something like this:
public class JobResultDTO
{
[Key]
public string Id { get; set; }
public string Created { get; set; }
public string Finished { get; set; }
public string Status { get; set; }
public string PlantLink { get; set; }
public IEnumerable<string> Messages { get; set; }
public string JobType { get; set; }
}
private void ParseJobResultXml(string jobResultXml, JobResultDTO jobDto)
{
try
{
var xmlElement = XElement.Parse(jobResultXml);
if (xmlElement != null)
{
jobDto.Finished = xmlElement.Element("Date").Value;
jobDto.Status = xmlElement.Element("Status").Value;
jobDto.PlantLink = xmlElement.Element("PlantLink").Value;
jobDto.Messages = xmlElement.Element("Messages").Elements("Message").Select(m => m.Value);
}
}
catch { }
}
var jobsAndResults = _context.Jobs.Where(j => j.JobType == jobOpenPlant || j.JobType == jobNormSite)
.AsEnumerable()
.Where(j => JobResultXmlHelper.JobBelongsToUser(j.JobResult, userLogin))
.OrderByDescending(j => j.JobCreated)
.Select(j => new
{
Result = j.JobResult,
Dto = new JobResultDTO
{
Id = j.Id.ToString(),
JobType = j.JobType,
Created = (j.JobCreated ?? DateTime.Now).ToString()
}
});
foreach (var j in jobsAndResults)
{
ParseJobResultXml(j.Result, j.Dto);
DumpDTO(j.Dto); //I see it set up correctly here
}
jobs.AddRange(jobsAndResults.Select(j => j.Dto));
DumpDTO(jobs.ElementAt(0)); //Now only the Key property is set
return jobs;
This is Debug output I'm seeing on the server for the two DEBUG lines
On Server...
Id: 51a8d041-5dff-4849-9651-9fb2fe89816a Status: Finished
Catalog - Updated 0 record(s) successfully:
Model - Updated 0 record(s) successfully:
On Server...
Id: 51a8d041-5dff-4849-9651-9fb2fe89816a Status:
As you can see the 2nd one has no entry for Status coln. Any ideas why this is happening?
I think your code is correct, but you don't have waiting time.
I didn't see any LoadOperation in your syntax.
Your code will work in WPF, but not in SilverLight.
*Update:*Try
foreach (var j in jobsAndResults)
{
ParseJobResultXml(j.Result, j.Dto);
DumpDTO(j.Dto); //I see it set up correctly here
jobs.Add(j.Dto);
}

Categories