Using RefactorThis.GraphDiff library - c#

I have the following tables in my database
And a graph mapping of (table address not shown in image):
.OwnedEntity(s => s.Address)
.OwnedCollection(s => s.SiteActivityPlants)
.OwnedCollection(s => s.SiteActivityPlants, with => with.AssociatedCollection(d => d.SiteActivityPlantNotes))
What I am trying to do is update the data in table SiteActivityPlantNotes.
After looking at Documentation, AssociatedCollection just updates the reference, what I need to do is update the data records within SiteActivityPlantNotes.
Is this possible?

2 Changes in your code should do it:
Removed second line ".OwnedCollection(s => s.SiteActivityPlants)"
as it is already part of the third line
Changed in third line "with.AssociatedCollection" to
"with.OwnedCollection"
Final Code:
.OwnedEntity(s => s.Address)
.OwnedCollection(s => s.SiteActivityPlants, with => with.OwnedCollection(d => d.SiteActivityPlantNotes))

Related

EF Core 7 - query with multiple includes enumerates entities multiple times

We have this database schema
and a test case where we search for a Connection and test the uniqueness of all associated Departures by Id.
In EF Core 6, everything was correct - we got the correct data (each Departure entry once), but after upgrading to EF Core 7 (7.0.0 or 7.0.2), the result now lists some Departures multiple times and we end up with the test failing as some Ids are present twice.
The test code:
// ...preparing DI
this.lines = scope.ServiceProvider.GetRequiredService<ILinesRepository>();
lines.Load(); // heavy task loading the data into memory for about 2 minutes (i know it's not really good but it's not my code...)
var connection = lines.GetConnection(lineNumber, connectionNumber); // filtering the data loaded in previous step
var groups = connection.Departures.GroupBy(d => d.Id); // finding the number of distinct IDs
Assert.Equal(groups.Count(), connection.Departures.Count);
lines.Load(DateTime? forDate = null):
var date = forDate ?? DateTime.Today;
// ... fetching some other data
_departures = DeparturesSet
.Include(d => d.TariffStation)
.ThenInclude(ts => ts.BackwardPlatform)
.Include(d => d.TariffStation)
.ThenInclude(ts => ts.ForwardPlatform)
.Include(d => d.TariffStation)
.ThenInclude(ts => ts.Station)
.Include(d => d.Connection)
.ThenInclude(l => l.Line)
.ThenInclude(l => l.Validity)
.Where(d => !d.Connection.Line.ValidTo.HasValue || d.Connection.Line.ValidTo.Value >= date)
.AsSplitQuery()
.ToArray();
// ... fetching some other data
ConnectionEntity? GetConnection(long lineNumber, int connectionNumber):
_departures?.FirstOrDefault(d =>
d.Connection.Line.IsValid(DateTime.Now) && d.Connection.Number == connectionNumber && d.Connection.Line.Number == lineNumber)?.Connection;
I've tried commenting out different parts in the _departures query except for includes of Connection and Line because without those the test fails with null ref.
The test fails whenever the query contains at least one .Include(d => d.TariffStation) (even without the ThenInclude part).
It seems that the TariffStation which is getting included is also fetching all its Departures which are not specified in the entity class nor its mapping.
The configuration of the relation between TariffStationEntity and DepartureEntity (b: DepartureEntity):
builder.HasOne(b => b.TariffStation)
.WithMany()
.HasForeignKey(b => b.TariffStationId)
.OnDelete(DeleteBehavior.Restrict);

Querying a deep child object with a certain property, but returning the root object which cascades down

Sorry about the cryptic heading, I have always struggled with this problem.
Say we have a model structure like so:
Schools, which have classes, which in turn have students. I'd like to return a list of schools (as the top-level object), including classes and students where the students are male.
The easy way to do this would be:
var maleStudents = Context.Students
.Include(s => s.Classes)
.ThenInclude(c => c.School)
.Where(s => s.Gender == Gender.Male)
Problem is, this returns a bunch of students with their own duplicate copies of the classes and schools. It also makes it hard to visually display them via 'School' without a bunch of organizing.
Is there an elegant solution or does it require my usual juggle after pulling it all to the server?
As of EF Core 5.0, filtered includes are supported, so you could write your query as follows:
var schools = Context.Schools
.Include(s => s.Classes)
.ThenInclude(c => c.Students.Where(s => s.Gender == Gender.Male));
If your targeting previous versions of EF Core your existing query is fine, as you could perform a GroupBy in the client to project your structure:
var schoolsWithStudents = maleStudents
.GroupBy(x => x.Classes
.First().School);
Although this assumes that each student only belongs to classes relating to the same school.
Update after comment
If you only want to bring back schools that have Male students, you can add an extra Where after the filtered include:
var schools = Context.Schools
.Include(s => s.Classes)
.ThenInclude(c => c.Students.Where(s => s.Gender == Gender.Male))
.Where(s => s.Classes.Any(c => c.Students.Any(st => st.Gender == Gender.Male)));

NHibernate map by code keycolumn?

just started on a project to convert a NHibernate Fluent mapping into the NHibernate Mapping By Code as part of an upgrade associated with one of my old applications.
Almost there, but I stumbled upon something I can't convert properly and found myself stumped. Now I hope maybe some of you experienced out there can help me with it.
Below is the original mapping using Fluent:
HasMany<ExampleEntity>(x => x.OtherExampleEntities)
.OptimisticLock.False()
.AsSet()
.KeyColumn("ParentExampleEntityId")
.Inverse()
.Cascade.SaveUpdate();
Now I got stuck on converting the KeyColumn-part of my mapping, below is my current progress (thus keyColumn still being there):
Set(x => x.OtherExampleEntities, x => {
x.OptimisticLock(false);
x.KeyColumn("ParentExampleEntityId");
x.Inverse(true);
x.Cascade(Cascade.Persist);
}, map => map.OneToMany(r => r.Class(typeof(ExampleEntity))));
There's not a lot of documentation regarding the mapping by code part of NHibernate but I've been spending a lot of time with the posts made by notherdev (#blogspot.se). All help is appreciated.
There is a comprehensive post about <set> mapping
Mapping-by-Code - Set and Bag by Adam Bar
Small snippet, but please observe the post:
Set(x => x.Users, c =>
{
c.Fetch(CollectionFetchMode.Join); // or CollectionFetchMode.Select,
// CollectionFetchMode.Subselect
c.BatchSize(100);
c.Lazy(CollectionLazy.Lazy); // or CollectionLazy.NoLazy, CollectionLazy.Extra
c.Table("tableName");
c.Schema("schemaName");
c.Catalog("catalogName");
c.Cascade(Cascade.All);
c.Inverse(true);
c.Where("SQL command");
c.Filter("filterName", f => f.Condition("condition"));
c.OrderBy(x => x.Name); // or SQL expression
c.Access(Accessor.Field);
c.Sort<CustomComparer>();
c.Type<CustomType>();
c.Persister<CustomPersister>();
c.OptimisticLock(true);
c.Mutable(true);
<key column="" ...> mapping:
c.Key(k =>
{
k.Column("columnName");
// or...
k.Column(x =>
{
x.Name("columnName");
// etc.
});
k.ForeignKey("collection_fk");
k.NotNullable(true);
k.OnDelete(OnDeleteAction.NoAction); // or OnDeleteAction.Cascade
k.PropertyRef(x => x.Name);
k.Unique(true);
k.Update(true);
});
....

Why aren't my included entities actually included?

I'm using Entity Framework 4 in Visual Studio 2010, with C#.
I have a method used in a repository that returns a object set with various navigation properties included. Up until recently, this method looked like this...
private IEnumerable<VRTSystem> GetSystems() {
return ctx
.Include(s => s.Customer.CustomerType)
.Include(s => s.VRTSystemProductConfigurations);
}
...where ctx is an ObjectSet of generic type VRTSystem. The full method had a lot more .Include()s than this, but this is enough to show the point.
This worked fine, however I needed to add some code to ensure that only VRTSystemProductConfigurations that had the Active flag set to true were returned. Following the advice commonly given for such situations, I changed the code to look like this...
private IEnumerable<VRTSystem> GetSystems() {
return ctx
.Include(s => s.Customer.CustomerType)
.Include(s => s.VRTSystemProductConfigurations)
.Select(s => new {
System = s,
VRTSystemProductConfigurations = s.VRTSystemProductConfigurations.Where(pc => pc.Active)
})
.Select(s => s.System);
}
However, this new version does not include any of the navigation properties, they are all null.
Anyone any idea why?
This is because Entity Framework is not entirely stupid. It sees that in the end only Systems are queried, so it cuts everything in between and returns Systems only. And part of the trick you're executing here is to disable lazy loading, so the navigation properties are null and will remain null.
You have to remove the last Select out of the scope of the EF query provider by adding an AsEnumerable:
return ctx
.Include(s => s.Customer.CustomerType)
.Select(s => new {
System = s,
VRTSystemProductConfigurations = s.VRTSystemProductConfigurations.Where(pc => pc.Active)
})
.AsEnumerable()
.Select(s => s.System);
And you don't want to include VRTSystemProductConfigurations, because that's the collection you want to load partly.

Is it possible to get associated like 0..1 data by RIA Services?

I can get 1..0 data by RIA Services fine.
var sFiltered = this.ObjectContext.Sequences.Include("Schedules").Include("Events")
.Include("Events.EventFrames")
.Include("Events.EventRules")
.Include("Events.EventFrames.EventFramePlugins")
.Include("Events.EventFrames.EventFramePlugins.EventFramePluginParameters")
.Include("Events.EventFrames.EventFramePlugins.EventFramePluginContentItems")
.Where(s => s.ID == schedule.SequenceID).FirstOrDefault();
So the code above works great.
The problem is that I want to get data by ClientContentItemID [EventFramePlugins] from [ClientContentItemElements]
Please have a look at the image below. But what I don't like to do is to use 1 extra request from the WPF client to get this data. So the idea is use 1 request to get ALL data I need.
Thank you!!!
I think the following query would return the desired result. it finally selects an anonymous type (ClientContentItemElementId, Sequence) which you can change to get the appropriate result. However, I did not test the generated sql to see if this approach is acceptable at all.
sequences.Include.....Where(s => s.ID == schedule.SequenceID).SelectMany(s => s.Events).SelectMany(e => e.EventFrames).SelectMany(ef => ef.EventFramePlugins)
.SelectMany(efp => efp.EventFramePluginContents).SelectMany(efpc => efpc.ClientContentItems).
SelectMany(cci => cci.ClientContentItemElemts).Where(ccie => ccie.ClientContentItemElementId == myValue).
Select(
ccie =>
new
{
ccie,
ccie.ClientContentItem.EventFramePluginContentItem.EventFramePlugin.EventFrame.Event.
Sequence
});

Categories

Resources