I have an Entity Framework Core project that uses generic repositories and UnitOfWork and is working as expected.
The database is one to many and related by IDs.
The RTCTrials entity contains a FK CourseID related to RTCCourses PK. When loading trials I am trying to get the course name in the datagrid and only achieved by using a union. Is this inefficient and a simpler approach. Ideally I would add a dropdownlist column populated with RTCCourses in the trials grid template and the CourseID in the trials table would select the correct id and show the ValueMember course name.
This is what the current method looks like:
using (var context = new RTCContext())
{
var factory = new EntityFrameworkUnitOfWorkFactory(context);
var unit = factory.Create();
var festivals = unit.RTCFestivals.All().ToList();
var trials = unit.RTCTrials.All().ToList();
var courses = unit.RTCCourses.All().ToList();
var trialcourses = trials.Join(courses, courses => courses.CourseID, trials => trials.CourseID, (trials, courses) => new
{
TrialID = trials.TrialID,
FestivalID = trials.FestivalID,
CourseID = trials.CourseID,
Trial = trials.Trial,
Course = courses.CourseName,
TrialGrade = trials.TrialGrade,
TrialDistance = trials.TrialDistance,
TrialAge = trials.TrialAge,
TrialHurdles = trials.TrialHurdles,
TrialAllowances = trials.TrialAllowances,
TrialMonth = trials.TrialMonth,
TrialActualDate = trials.TrialActualDate,
TrialActualTime = trials.TrialActualTime,
TrialRaceCard = trials.TrialRaceCard,
TrialQualifiers = trials.TrialQualifiers
}).ToList();
this.radGridViewFestivalDestinations.DataSource = festivals;
this.radGridViewFestivalDestinations.Templates[0].DataSource = trialcourses;
foreach (GridViewDataColumn column in radGridViewFestivalDestinations.MasterTemplate.Columns)
{
column.BestFit();
}
foreach (GridViewDataColumn column in radGridViewFestivalDestinations.Templates[0].Columns)
{
column.BestFit();
}
}
RTCTrial Entity
public partial class RTCTrial {
public RTCTrial()
{
this.RTCResults = new List<RTCResult>();
this.RTCWeathers = new List<RTCWeather>();
OnCreated();
}
public virtual int TrialID { get; set; }
public virtual int FestivalID { get; set; }
public virtual int CourseID { get; set; }
public virtual string Trial { get; set; }
public virtual string TrialGrade { get; set; }
public virtual string TrialDistance { get; set; }
public virtual string TrialAge { get; set; }
public virtual int? TrialHurdles { get; set; }
public virtual string TrialAllowances { get; set; }
public virtual string TrialMonth { get; set; }
public virtual DateTime? TrialActualDate { get; set; }
public virtual TimeSpan? TrialActualTime { get; set; }
public virtual string TrialRaceCard { get; set; }
public virtual int TrialQualifiers { get; set; }
public virtual RTCCourse RTCCourse { get; set; }
public virtual RTCFestival RTCFestival { get; set; }
public virtual IList<RTCResult> RTCResults { get; set; }
public virtual IList<RTCWeather> RTCWeathers { get; set; }
#region Extensibility Method Definitions
partial void OnCreated();
#endregion
}
RTCCourse Entity
public partial class RTCCourse {
public RTCCourse()
{
this.RTCTrials = new List<RTCTrial>();
OnCreated();
}
public virtual int CourseID { get; set; }
public virtual string CourseName { get; set; }
public virtual string CourseCountry { get; set; }
public virtual string CourseDirection { get; set; }
public virtual string CourseCharacteristics { get; set; }
public virtual string CourseAlternateName { get; set; }
public virtual double CourseLat { get; set; }
public virtual double CourseLong { get; set; }
public virtual IList<RTCTrial> RTCTrials { get; set; }
#region Extensibility Method Definitions
partial void OnCreated();
#endregion
}
Regards, Neil
Suggestion would be on the returned courses you would want each course to have its associated trials. In the unit of work that returns all courses - possibly have an option to include them. Your dropdown would bind to each course and your grid would bind to the list of trials in the selected course.
public IEnumerable<RTCCourse> All(bool includeTrials = false)
{
var q = context.RTCCourses;
if (includeTrials)
{
q = q.Include(c => c.RTCTrials)//.ThenInclude(t => t.RTCResults)
;
}
return q.AsEnumerable(); // assuming that is the returned type
}
That should allow your courses to have the list of trials set. Then there is no need to get all trials. And you can bind to courses (and list of trials within each) directly instead of doing the join and binding to the anonymous.
Of 'course' -- this is merely a suggestion ;)
Related
Song has a many to many relationship with it's self. I store these ids in a class called SimilarVersion with both id columns.
public class Song
{
public int Id { get; set; }
public string AudioName { get; set; }
public string ArtistName { get; set; }
...
public virtual ICollection<SimilarVersion> SimilarVersions { get; set; } = new List<SimilarVersion>();
}
public class SimilarVersion
{
public int Id { get; set; }
public int? Song_Id1 { get; set; }
}
View Models:
public class SongDto
{
public int Id { get; set; }
public string AudioName { get; set; }
public string ArtistName { get; set; }
...
public ICollection<SimilarSongDto> SimilarSongDtos { get; set; } = new List<SimilarSongDto>();
}
public class SimilarSongDto
{
public int Id { get; set; }
public string AudioName { get; set; }
public string ArtistName { get; set; }
...
}
The SimilarVersion table in my database now has Id,Song_Id,Song_Id1, as EF has generated Song_Id. How do I get to use that EF generated column in my code though?
_similarVersionService.GetSimiliarVersion().Song_Id will give me an error because there is no property in the class called that. I could manually add it to the class like I have done with Song_Id1 and remove the collection from the other class but I think I must be doing something wrong. Also please tell me if there is a better way of mapping this.
I tried adding public int Song_Id { get; set; } and it just made another column in my table called Song_Id2.
public ActionResult Song(int id)
{
//Map the domainModel to songViewModel
var songDto = Mapper.Map<Song, SongDto>(_songService.GetSong(id));
//Get all of the songs where the id == the Song_Id column in similar version table
var songs = _songService.GetSongs().ToList()
.Where(x => x.SimilarVersions.Any(z => z.Song_Id == songDto.Id))
.ToList(); //z.Song_Id no definition found
//Map the Song domain to SimilarSong ViewModel and assign it to the songDto to be passed to the view
songDto.SimilarSongDtos = Mapper.Map<ICollection<Song>, ICollection<SimilarSongDto>>(songs);
return View(songDto);
}
Edit. Trying to add to a row based on Admir answer:
var songToUpload = new Song
{
AudioName = uploadSongDtos[i].AudioName.Trim(),
ArtistName = uploadSongDtos[i].ArtistName,
};
foreach (var compareAgainstString in _songService.GetSongs().ToDictionary(x => x.Id, x => x.AudioName))
{
var score = SearchContext.Levenshtein.iLD(songToUpload.AudioName, compareAgainstString.Value);
//Don't add the current song
if (score < 50 && songToUpload.Id != compareAgainstString.Key)
songToUpload.SimilarVersionsWhereSimilar.Add(new SimilarVersion { SimilarId = compareAgainstString.Key });
}
Both OriginalId and SimilarId are assigned to whatever the id of songToUpload.Id is given the relationship we defined in models, which is correct for OriginalId but it is also overriding my custom set SimilarId above. How can I stop this?
You can take this approach:
public class Song
{
public int Id { get; set; }
public string ArtistName { get; set; }
public virtual IList<Similarity> SimilaritiesWhereOriginal { get; set; }
public virtual IList<Similarity> SimilaritiesWhereSimilar { get; set; }
}
public class Similarity
{
public int Id { get; set; }
public int OriginalId { get; set; }
public virtual Song Original { get; set; }
public int SimilarId { get; set; }
public virtual Song Similar { get; set; }
}
public class ApplicationDbContext : DbContext
{
public DbSet<Song> Songs { get; set; }
public DbSet<Similarity> Similarities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Song>().HasMany(x => x.SimilaritiesWhereOriginal).WithRequired(x => x.Original).WillCascadeOnDelete(false);
modelBuilder.Entity<Song>().HasMany(x => x.SimilaritiesWhereSimilar).WithRequired(x => x.Similar).WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
}
Similarity class shows relationship between "original" song and "similar" song. This class replaces EF auto-generated table with your own many-2-many relationship table that you can access from the code.
It is likely the ID is actually generated by your Store. Call Context.SaveChanges() to create it, then read the ID.
I am using Entity Framework 6.1 in asp.net webform project. When I try to add new object into projects, I am getting Ambiguous match found exception.
I am using database first approach. I do not any manipulation in created models. I have read some questions about that problem. General cause is same named properties and navigation in class. I did not found any same named property.
Can you tell me what i missed?
Thank you
Code:
projects m_NewProject = new projects();
decimal m_CompanyRef = MemberHelperC.getUser().CompanyRef;
DateTime m_EndDate = GeneralHelperC.getCompanyDateTime().AddDays(5);
DateTime m_StartDate = GeneralHelperC.getCompanyDateTime();
customers m_Customer = myEntity.customers.Where(xXx => xXx.CompanyRef == m_CompanyRef).FirstOrDefault();
m_NewProject.ProjectLeadRef = MemberHelperC.getUserID();
m_NewProject.ProjectName = m_ProjectName;
m_NewProject.ProjectStatus = Convert.ToByte(1);
m_NewProject.SourceLangRef = Convert.ToDecimal(comboSourceLang.SelectedValue);
m_NewProject.TargetLangRef = Convert.ToDecimal(comboTargetLang.SelectedValue);
m_NewProject.DomainRef = Convert.ToDecimal(1);
m_NewProject.ProjectYear = GeneralHelperC.getCompanyDateTime()/*DateTime.Now*/.Year;
m_NewProject.EndDate = m_EndDate;
m_NewProject.StartDate = m_StartDate;
m_NewProject.TaskStepNameRef = m_TaskStepNameID;
m_NewProject.CustomerRef = Convert.ToDecimal(m_Customer.RID);
Random m_Random = new Random();
m_NewProject.ProjectUniqueID = m_Random.Next(0, 99999999);
m_NewProject.ProjectTBX = m_Dictionary.RID;
myEntity.projects.Add(m_NewProject);//exception occur this method
myEntity.SaveChanges();
Object Class:
public partial class projects
{
public projects()
{
this.projectnotes = new HashSet<projectnotes>();
this.projectpriceoffers = new HashSet<projectpriceoffers>();
this.projectreferencedoc = new HashSet<projectreferencedoc>();
this.projects1 = new HashSet<projects>();
this.taskstepexceptions = new HashSet<taskstepexceptions>();
this.tbxrelation = new HashSet<tbxrelation>();
this.tmproject = new HashSet<tmproject>();
this.tmrelation = new HashSet<tmrelation>();
this.wizardprojecttasks = new HashSet<wizardprojecttasks>();
this.works = new HashSet<works>();
}
public decimal RID { get; set; }
public string ProjectName { get; set; }
public decimal ProjectStatus { get; set; }
public decimal ProjectLeadRef { get; set; }
public System.DateTime EndDate { get; set; }
public System.DateTime StartDate { get; set; }
public int ProjectYear { get; set; }
public int ProjectUniqueID { get; set; }
public Nullable<decimal> ParentProjectRef { get; set; }
public Nullable<decimal> TMXHeaderRef { get; set; }
public decimal SourceLangRef { get; set; }
public decimal TargetLangRef { get; set; }
public decimal DomainRef { get; set; }
public decimal TaskStepNameRef { get; set; }
public Nullable<decimal> ProjectTBX { get; set; }
public Nullable<decimal> CustomerRef { get; set; }
public virtual customers customers { get; set; }
public virtual domainname domainname { get; set; }
public virtual language language { get; set; }
public virtual language language1 { get; set; }
public virtual ICollection<projectnotes> projectnotes { get; set; }
public virtual ICollection<projectpriceoffers> projectpriceoffers { get; set; }
public virtual ICollection<projectreferencedoc> projectreferencedoc { get; set; }
public virtual ICollection<projects> projects1 { get; set; }
public virtual projects projects2 { get; set; }
public virtual projectstatus projectstatus1 { get; set; }
public virtual tasknames tasknames { get; set; }
public virtual tbxdictionary tbxdictionary { get; set; }
public virtual tmxheaderinterface tmxheaderinterface { get; set; }
public virtual users users { get; set; }
public virtual ICollection<taskstepexceptions> taskstepexceptions { get; set; }
public virtual ICollection<tbxrelation> tbxrelation { get; set; }
public virtual ICollection<tmproject> tmproject { get; set; }
public virtual ICollection<tmrelation> tmrelation { get; set; }
public virtual ICollection<wizardprojecttasks> wizardprojecttasks { get; set; }
public virtual ICollection<works> works { get; set; }
}
}
Are you sure there are no same named property somewhere with different casing? It compiles as case sensative, but executes as case insensitive, so even a few capital letters difference in a same name property could cause a Ambiguous match found exception.
I had the same problem:
I had a table named service linked to the table order.
Inside my partial class order, I was using a property called Service.
//Auto generated:
public virtual service service { get; set; }
//My custom property (Shortned, there was a big String.Format inside):
public String Service { get { return service.ds_name; }}
Case insensitive. :/
TL;DR: Property inside partial class with the same name, but with diffferent caps.
I solved my problem. There is a really ambiguous in my code. CustomerStatus really dublicated.
I think Entity Framework team should improve Ambiguous match found exception details. I have more than 90 tables and very hard to debugging...
Exception occure of these code:
public partial class customers
{
public enum CustomerStatusEnum : long
{
Closed = 3,
Open = 1,
Potential = 2
}
public CustomerStatusEnum CustomerStatus
{
get
{
return (CustomerStatusEnum)Status;
}
}
}
You can see my previous question which related with many to many relation but with auto generated mapping table.
I have 2 model, HrTraining and HrPerson. Any people can be assigned to one or more Trainings. You can see my model as below
public class HrTraining
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
public class HrMapTrainingPerson
{
public int Id { get; set; }
public string Status { get; set; }
public int HrTrainingId { get; set; }
public int HrPersonId { get; set; }
public virtual HrTraining HrTraining { get; set; }
public virtual HrPerson HrPerson { get; set; }
}
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
How can i take all training objects which assingned to a person with efficient way.
So you want to find a person, and get all the trainings assigned to it? There are lot of ways.. but using your models, this could be something like this
var trPersons = dbContext.HrPerson.Find(idPerson).HrMapTrainingPerson.ToList();
foreach(var trPerson in trPersons) {
var training = trPerson.HrTraining;
//do what you want, here you can get trPerson.HrTraining.Name for instance
}
When I select artile, it select user, but user has a collection of article, so article select user again. May be recursive cause out of memory ,
The calling processing is :
article=>user=>article=>user...
ef entities is :
public partial class article
{
public int id { get; set; }
public string title { get; set; }
public string cont { get; set; }
public Nullable<int> uid { get; set; }
public System.DateTime addtime { get; set; }
public Nullable<int> colid { get; set; }
public virtual user user { get; set; }
public virtual column column { get; set; }
}
public partial class user
{
public user()
{
this.roleusers = new HashSet<roleuser>();
this.articles = new HashSet<article>();
}
public int id { get; set; }
public string email { get; set; }
public string uname { get; set; }
public string upass { get; set; }
public virtual ICollection<roleuser> roleusers { get; set; }
public virtual ICollection<article> articles { get; set; }
}
mysql EF operation class is :
public class ArtDao
{
readonly crmEntities _ent = new crmEntities();
public List<article> PageArts(int start, int limit, out int total)
{
var ll =
_ent.articles.OrderByDescending(o => o.id)
.Skip(start)
.Take(limit)
.ToList();
total = _ent.articles.Count();
return ll;
}
}
How to avoid to eager load the collection property roleusers and articles ?
You need to set LazyLoad on you edmx properties, and manually load only first level childs with Include() method when selecting:
public List<article> PageArts(int start, int limit, out int total)
{
var ll =
_ent.articles.OrderByDescending(o => o.id)
.Skip(start)
.Take(limit)
.Include(o => o.user)
.ToList();
total = _ent.articles.Count();
return ll;
}
You need to implement it in another class, wich can be a partial class.
I have a database with 3 tables:
Subjects
Members
Topics
Then I added the connection string to web.config and created an EF with the following classes:
namespace MySite.Models
{
public class MySiteDBModel : DbContext
{
public DbSet<Topic> Topics { get; set; }
public DbSet<Subject> Subjects { get; set; }
public DbSet<Member> Members { get; set; }
public DbSet<TopicDataModel> TopicDataModel { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class Topic
{
[Key]
public int TopicID { get; set; }
public int SubID { get; set; }
public int MemberID { get; set; }
public string TDate { get; set; }
public string Title { get; set; }
public string FileName { get; set; }
public int Displays { get; set; }
public string Description { get; set; }
public virtual Subject Subject { get; set; }
public virtual Member Member { get; set; }
public virtual ICollection<TopicView> TopicView { get; set; }
}
public class Subject
{
[Key]
public int SubID { get; set; }
public string SubName { get; set; }
public virtual ICollection<Topic> Topic { get; set; }
}
public class Member
{
[Key]
public int MemberID { get; set; }
public string FLName { get; set; }
public string Email { get; set; }
public string Pwd { get; set; }
public string About { get; set; }
public string Photo { get; set; }
public virtual ICollection<Topic> Topic { get; set; }
}
public class TopicDataModel
{
[Key]
public int TopicID { get; set; }
public string SubName { get; set; }
public string FLName { get; set; }
public string TDate { get; set; }
public string Title { get; set; }
public int Displays { get; set; }
public string Description { get; set; }
}
}
Now when I am trying to query the database with the this code:
public ActionResult Index()
{
var topics = from t in db.Topics
join s in db.Subjects on t.SubID equals s.SubID
join m in db.Members on t.MemberID equals m.MemberID
select new TopicDataModel()
{
TopicID = t.TopicID,
SubName = s.SubName,
FLName = m.FLName,
TDate = t.TDate,
Title = t.Title,
Displays = t.Displays,
Description = t.Description
};
return View(topics.ToList());
}
I got this Error:
The model backing the 'MySiteDBModel' context has changed since the
database was created. Either manually delete/update the database, or
call Database.SetInitializer with an IDatabaseInitializer instance.
For example, the DropCreateDatabaseIfModelChanges strategy will
automatically delete and recreate the database, and optionally seed it
with new data.
Please help me!!!!!!
You need to set some controls on how EF is handling changes to your data model. Julie Lerman has a good blog post on Turning Off Code First Database Initialization Completely.
Also, here is a good overview - Inside Code First Database Initialization