I have an entityA that contains a EntityCollection of entityB.
I updated the metdata for EntityA with the [include] decoration over the line defining the entityB as follows:
[Include]
public EntityCollection<daily> daily { get; set; }
in my domainservice class I have a function to retrieve entityA as follows:
var summary =
(from S in ObjectContext.summery.Include("daily")
where S.daily_number == daily_number
&& S.month_number == month_number
&& S.period_id == period_id
select S).FirstOrDefault();
return summary;
From the Client I always get count Zero of entityB.
What am I missing here !!
Best regards
You'll need to define the association between summery and daily with the AssociationAttribute.
[Include]
[Association("SomeUniqueName", "summery_id", "parent_summery_id", IsForeignKey = false)]
public EntityCollection<daily> daily { get; set; }
I am making the following assumptions about your classes
public class summery
{
...
public int? summery_id { get; set; }
...
}
public class daily
{
...
// Foreign key to the parent summery
public int parent_summery_id { get; set; }
...
}
Edit:
In response to Waleed's comment, a composite foreign key association could look like
[Association("SomeUniqueName",
"summery_field1, summary_field2, summary_field3",
"parent_summary_filed1, parent_summary_filed2, parent_summary_filed3",
IsForeignKey = false)]
Related
I have a many-to-many relationship between User(Contributor) and TaskType. I want to assign to a variable only TaskTypes that have the current user among their contributors. Obviously, I can somehow do this using the functionality of the Entity Framework. But how? I use asp.net core 3.
Below I try unsuccessfully to do it:
public IQueryable<TaskType> ContributedTaskTypes
{
get
{
// This code doesn't work!
return _dbContext.TaskTypes.Where(t => t.Contributors.Contains(c => c.UserId == CurrentUserId));
}
}
Below are definitions of all models involved in this relationship:
public class TaskType
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public virtual List<Contribution> Contributors { get; set; }
}
public class Contribution
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int TaskTypeId { get; set; }
public TaskType TaskType { get; set; }
}
public class ApplicationUser : IdentityUser
{
public virtual List<Contribution> ContributedToTaskTypes { get; set; }
}
For those queries it is always easiest to do queries where you can dot to the result.
Here is the query with sql-like syntax
from row in _dbContext.Contribution
where row.UserId == CurrentUserId
select row.TaskType
By selecting row.TaskType instead of just row you get it correct entity.
Is that Contributors property retrieved correctly from DB? if it is not you must call Include() method to load/refer relational referenced entities
_dbContext.TaskTypes.Include(p=>p.Contributors).Where(..
more: https://learn.microsoft.com/en-us/ef/core/querying/related-data
In Addition, if EF Core Table Relation is not correctly defined, you should follow
this instruction: https://www.entityframeworktutorial.net/efcore/configure-many-to-many-relationship-in-ef-core.aspx
I've a situation where entity framework core (2.0) is performing additional work to a parent table when I update a row in a child table. I've pin-pointed the cause to a value not being set in the unflattened object tree produced by AutoMapper (I'm not saying it is an error in AutoMapper; it's probably more to do with my code).
I'm using ASP.NET Core 2.0, C#, EF Core 2.0 and AutoMapper for the API development side. The database already exists and the EF classes scaffolded from it.
To keep it short, the child table is Note and the parent table is NoteType. The EF classes (extraneous columns removed) are as follows :
//Entity classes
public partial class Note
{
public int NoteBookId { get; set; }
public short NoteSeqNo { get; set; }
public short NoteTypeId { get; set; }
public string NoteText { get; set; }
public NoteBook NoteBook { get; set; }
public NoteType NoteType { get; set; }
}
public partial class NoteType
{
public NoteType() { Note = new HashSet<Note>(); }
public short NoteTypeId { get; set; }
public string NoteTypeDesc { get; set; }
public ICollection<Note> Note { get; set; }
}
//DTO class
public class NoteDto
{
public int NoteBookId { get; set; }
public short NoteSeqNo { get; set; }
public short NoteTypeId { get; set; }
public string NoteTypeNoteTypeDesc { get; set; }
public string NoteText { get; set; }
}
public class NoteTypeDto
{
public short NoteTypeId { get; set; }
public string NoteTypeDesc { get; set; }
}
(NoteBookId + NoteSeqNo) is Note's primary key.
NoteTypeId is the NoteType's primary key.
Configuration
This is the AutoMapper configuration:
// config in startup.cs
config.CreateMap<Note,NoteDto>().ReverseMap();
config.CreateMap<NoteType,NoteTypeDto>().ReverseMap();
Read the data
As a result of data retrieval I get the expected result and the parent note type description is populated.
// EF get note in repository
return await _dbcontext.Note
.Where(n => n.NoteId == noteId && n.NoteSeqNo == noteSeqNo)
.Include(n => n.NoteType)
.FirstOrDefaultAsync();
// Get note function in API controller
Note note = await _repository.GetNoteAsync(id, seq);
NoteDto noteDto = Mapper.Map<NoteDto>(note);
Example JSON result:
{
"noteBookId": 29,
"noteSeqNo": 19,
"noteTypeId": 18,
"noteTypenoteTypeDesc": "ABCDE",
"noteText": "My notes here."
}
Update the data
When the process is reversed during an update, the API controller maps the dto to the entity
Mapper.Map<Note>(noteDto)
Then when it is passed to EF by the repository code, EF tries to add a NoteType row with id 0. The unflattened object tree looks like this:
Note
NoteBookId = 29
NoteSeqNo = 19
NoteTypeId = 18
NoteTypeNoteTypeDesc = "ABCDE"
NoteText = "My notes updated."
NoteType.NoteTypeDesc = "ABCDE"
NoteType.NoteTypeId = 0
The parent id column (NoteType.NoteTypeId) value is 0 and is not assigned the value of 18 which is what I expected.
(During debugging I manually set NoteType.NoteTypeId to 18 to ensure EF did nothing with it).
To work around this at the moment I nullify the NoteType in the Note in the repository code.
Should I expected AutoMapper to populate all the parent properties with setters or have I missed some configuration? Perhaps there is a glaring flaw in my approach?
When AutoMapper reverses the mapping, it has to collect all information for nested objects from the flat object. Your DTO only carries a value for the mapping NoteType -> NoteTypeDesc. Not for NoteType -> NoteTypeId, so AM really doesn't have any idea where to get that value from.
If you want to rely on flattening only, the only way to change that is to add a flattened NoteTypeId to the DTO besides the unflattened one:
public class NoteDto
{
public int NoteBookId { get; set; }
public short NoteSeqNo { get; set; }
public short NoteTypeId { get; set; } // Not flattened
public short NoteTypeNoteTypeId { get; set; } // Flattened
public string NoteTypeNoteTypeDesc { get; set; }
public string NoteText { get; set; }
}
The alternative is to add this to your mapping:
config.CreateMap<Note, NoteDto>()
.ForMember(dest => dest.NoteTypeId,
e => e.MapFrom(src => src.NoteType.NoteTypeId))
.ReverseMap();
MapFrom-s (including the default unflattening) are reversed now. You can drop ReverseMap and create the maps, ignore Note.NoteType or ignore the offending path, Note.NoteType.NoteTypeDesc.
I have 2 tables in my DB like Patient Table and Appointment Table
for which I have created 2 Data Contracts namely Patient,Appointment in my WCFREST Service.
I use List<Patient> to fill the DB Table info in the List object and
List<Appointment> to fill the DB Table info in List obj as well.
Here I have an Operation Contract which returns data from 2 tables using Join Statement
For that how should I store the result, because I have 2 different objects ?
Do I need to create one more DataContract by combining fields of these two tables?
Use Entity frame work (table first or model first approach). But as you already have tables available you might want to base your model on the tables. Whatever way you choose ultimately your Data Contract should look as below:
[DataContract]
public class Patient
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public List<Appointment> Appointments { get; set; }
}
[DataContract]
public class Appointment
{
[DataMember]
public int ID { get; set; }
[DataMember]
public DateTime AppointmentTime { get; set; }
[DataMember]
public Address Venue { get; set; }
}
Trick is to make DataContract for each of table. You don't have to create multiple data contracts to match your needs.
The service contract should be as below:
[ServiceContract]
public interface IPatientService
{
[OperationContract]
List<Patient> GetAllPatients();
}
you can then use the DbContext provided by the EF in combination with extensions (Dependency injection) as below. Or just get rid of the constructor if you don't want to go advanced with it and create _context object right on intialization. Here is a sample.
public class PatientService : IPatientService
{
HospitalDbContext _context;
public PatientService(HospitalDbContext _context)
{
this._context = _context;
}
public List<Patient> GetAllPatients()
{
return (from patient in _context.Patients
select patient).ToList<Patient>();
}
}
You are free to add LINQ conditions here to take joins and other operations as well.
[DataContract]
class A
{
[DataMember]
public string PatientName { get; set; }
// more stuff.
}
[DataContract]
class B
{
[DataMember]
public string HospitalName { get; set; }
// more stuff.
}
[DataContract]
class C
{
[DataMember]
public A prop1 { get; set; }
[DataMember]
public B prop2 { get; set; }
// more stuff.
}
As entity framework states, "Code first", here we go with the code first...
public class BaseModel
{
[Key]
public Guid Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
public BaseModel()
{
this.Id = Guid.NewGuid();
this.DateCreated = DateTime.Now;
this.DateChanged = DateTime.Now;
}
}
public class Association: BaseModel
{
public string Name { get; set; }
public string Type { get; set; }
public virtual List<Rule> Rules { get; set; }
public Association()
: base()
{
}
}
public class Rule: BaseModel
{
[ForeignKey("Association")]
public Guid AssociationId { get; set; }
//[Required]
public virtual Association Association { get; set; }
//[Required]
public string Name { get; set; }
public string Expression { get; set; }
public virtual List<Action> Actions { get; set; }
public Rule()
: base()
{
}
}
public class Action: BaseModel
{
public string Name { get; set; }
public string ActionType { get; set; }
[ForeignKey("Rule")]
public Guid RuleId { get; set; }
public virtual Rule Rule { get; set; }
public int Order { get; set; }
public Action()
: base()
{
}
}
So these are my four model classes that are using entity framework code first.
Each inherit from the baseclass, so they all have an Id Guid as Primary Key.
An Association has a list of rules. (Rule has FK to Association)
A Rule as has a list of actions. (Action has FK to Rule)
What I would like to do is only change and save the most upwards class = Association.
For example when deleting a rule, I would like this code to work:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
Association ass = this.DataContext.Associations.FirstOrDefault(a => a.Id == assId);
ass.Rules.RemoveAll(r => r.Id == ruleId);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}
On the context.savechanges this is giving me this error:
'The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.'
This error also occurs when deleting an action.
Is there a way to change the most upper (Association) object AND ONLY changing things to this Association.
I DO NOT want to say context.Rules.remove(...) or context.actions.remove(...)
here's the source: http://server.thomasgielissen.be/files/mvctesting.zip
you need VS2012, all nuget packages are included in zip and you should be able to build and run the project.
Thanks in advance for your feedback!
Greetz,
Thomas
I you want to fix this issue, you should store your relations through junction tables. I don't think that you can achieve what you need, with this model.
However if you put a junction table(or entity) between your entities, you can easily remove child objects and update parent object.
For example, put a junction entity between Association and Rule:
public class AssociationRule: BaseModel
{
public Guid AssociationId { get; set; }
public Guid RuleId { get; set; }
[ForeignKey("AssociationId")]
public virtual Association Association { get; set; }
[ForeignKey("RuleId")]
public virtual Rule Rule { get; set; }
public Association()
: base()
{
}
}
Now, you can easily remove any rule from any association:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
AssociationRule assr = this.DataContext
.AssociationRuless
.FirstOrDefault(ar => ar.AssociationId == assId && ar.RuleId == ruleId);
this.DataContext.AssociationRules.Remove(assr);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}
I am trying to create my own foreign key convention that will name the FK in "FK_SourceTable_TargetTable" format.
However, when I run it I end up with two foreign keys instead of one.
My custom foreign key convention looks like this:
public class OurForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if (property == null)
return string.Format("FK_{0}Id", type.Name); // many-to-many, one-to-many, join
if (property.Name == type.Name)
return string.Format("FK_{0}_{1}", property.DeclaringType.Name, type.Name);
return string.Format("FK_{0}_{1}_{2}", property.DeclaringType.Name, property.Name, type.Name);
}
}
My code to exercise it:
[TestMethod]
public void ShouldBeAbleToBuildSchemaWithOurConventions()
{
var configuration = new Configuration();
configuration.Configure();
Fluently
.Configure(configuration)
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<Widget>()
.Conventions.Add<OurForeignKeyConvention>()
)
.BuildSessionFactory();
new SchemaExport(configuration).Create(false, true);
}
My classes and mappings:
public class Widget
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
public virtual WidgetType Type { get; set; }
public virtual ISet<WidgetFeature> Features { get; set; }
}
public class WidgetFeature
{
public virtual int Id { get; set; }
public virtual Widget Widget { get; set; }
public virtual string FeatureDescription { get; set; }
}
public class WidgetMap : ClassMap<Widget>
{
public WidgetMap()
{
Id(w => w.Id);
Map(w => w.Description);
HasMany(w => w.Features).Cascade.AllDeleteOrphan().Inverse();
}
}
public class WidgetFeatureMap : ClassMap<WidgetFeature>
{
public WidgetFeatureMap()
{
Id(w => w.Id);
Map(w => w.FeatureDescription);
References(w => w.Widget);
}
}
The end result is two foreign keys, one called what I want - FK_WidgetFeature_Widget - and another one called FK_WidgetId.
If I change OurForeignKeyConvention to always return the same name regardless of whether the "property" parameter is null then I correctly get a single FK - but I then cannot get the "SourceTable" part of my FK name.
Can anyone explain what I am doing wrong here? Why is GetKeyName called twice? And why does one of the calls not provide a value for the "property" parameter?
Doh. ForeignKeyConvention provides the name for the FK column. What I should have been using is the IHasManyConvention, which can be used to name the FK constraint itself.
public class OurForeignKeyConstraintNamingConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.ForeignKey(string.Format("FK_{0}_{1}", instance.Relationship.Class.Name, instance.EntityType.Name));
}
}