I've been using LinQ to SQL with EF for a while now and have just stumbled on some strange behaviour that hopefully someone may be able to explain for me.
I'm performing a LinQ query on a database context that has a POCO entity with virtual properties for related entities. I'm using a where clause to eliminate instances where that entity is null. I'm using Lazy Loading.
return this.runtimeContext.FatalExceptionLogs.Where(l => l.RuntimeToDesignJuicerRelationship != null);
What I'm finding is that when my query is evaluated LinQ to SQL seems to entirely ignore my condition that the virtual property is null as if I'd never included this check at all. Instead it returns all records in my dbset named FatalExceptionLogs.
Now I have a simple workaround for this which is to first load the data into memory using .ToList() then
This looks like so:
return this.runtimeContext.FatalExceptionLogs.ToList().Where(l => l.RuntimeToDesignJuicerRelationship != null);
Now the check is performed in memory and all instances where the virtual property is null are returned (because there is no corresponding record as the id which is used for the join is nullable) and we're all good.
I have also considered:
Checking if the id that is joined on is null but unfortunately I can't garauntee that the referential integrity of the table has been maintained as there is no foreign key applied urgh!.
Checking if there are any records in the other table with the matching id, but that could be rather inefficient.
So I have a way of working around this but I'd really like to understand why LinQ to Sql is doing this and what other options there are, can anyone help?
The full code if it helps is below though I've cut it down for this example:
The query:
return this.runtimeContext.FatalExceptionLogs.ToList().Where(l => l.RuntimeToDesignJuicerRelationship != null);
The entity:
public class FatalExceptionLog
{
public int Id { get; set; }
public int? RuntimeJuicerId { get; set; }
public virtual RuntimeToDesignJuicerRelationship RuntimeToDesignJuicerRelationship { get; set; }
}
The mapping:
public class FatalExceptionLogMap : EntityTypeConfiguration<FatalExceptionLog>
{
public FatalExceptionLogMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("FatalExceptionLogging");
this.Property(t => t.RuntimeJuicerId).HasColumnName("JuicerLiveID");
this.HasRequired(t => t.RuntimeToDesignJuicerRelationship)
.WithMany(t => t.FatalExceptionLogs)
.HasForeignKey(t => t.RuntimeJuicerId);
}
}
Why NOT just do the normal joining?
return this.runtimeContext.FatalExceptionLogs.Where(
l => runtimeContext.RuntimeJuicers.Any(
y => y.RuntimeJuicerId == l.RuntimeJuicerId
)
);
Related
I have 3 tables: users, posts, and likes. I want to do a lambda expression to return an array that holds username, postText, and liked (true or false)
var myList = _context.Posts.Join(_context.Users,
post => post.UserID_FK,
user => user.ID,
(post, user) => new { Post = post, User = user })
.Join(
_context.Likes,
u => u.User.ID,
likes => likes.UserID,
(u, likes) => new PostDTO
{
ID = u.Post.ID,
username = u.Patient.UserName,
Text = u.Post.Text,
Likes = u.Post.Likes,
liked = (likes.PostID == u.Post.ID && likes.UserID == userModel.ID)}
.OrderByDescending(d => d.Date);
return myList;
My problem is with my code I am getting everything I want, but i am getting repeated records. I am trying to understand why I am getting duplicate records? I have searched lambda expressions and I can not figure out where my issue is.
I thank you guys in advance!
This occurs because like with an SQL query joining tables you are building a Cartesian for all combinations. As a simple example of a Post with 2 Likes where we run an SQL statement with an inner join:
SELECT p.PostId, l.LikeId FROM Posts p INNER JOIN Likes l ON p.PostId == l.PostId WHERE p.PostId = 1
we would get back:
PostId LikeId
1 1
1 2
Obviously, returning more data from Post and Like to populate a meaningful view, but the raw data will look to have a lot of duplication. This compounds with the more 1-to-many relationships, so you need to manually interpret the data to resolve that you are getting back one post with two Likes.
When you utilize navigation properties correctly to map the relationships between your entities, EF will do the heavy lifting to turn these Cartesian combinations back into a meaningful model structure.
For example, take the following model structure:
public class Post
{
public int PostId { get; set; }
public DateTime Date { get; set; }
// ... Other Post-related data fields.
public virtual User Patient { get; set; }
public virtual ICollection<Like> Likes { get; set; } = new List<Like>();
}
public class Like
{
public int LikeId { get; set; }
public virtual Post Post { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
}
EF can likely work out all of these relationships automatically via convention, but I recommend being familiar with explicit configuration because there is always a time when convention fails in a particular situation where you want or need to use a different naming convention. There are four options for resolving relationships in EF: Convention, Attributes, EntityTypeConfiguration, and using OnModelCreating's modelBuilder. The below example outlines EntityTypeConfiguration via EF6. EF Code uses an interface called IEntityTypeConfiguration which works in a similar way but you implement a method for the configuration rather than call base-class methods in a constructor. Attributes and Convention are generally the least amount of work, but I typically run into situations they don't map. (They do seem to be more reliable with EF Core) Configuring via modelBuilder is an option for smaller applications but it gets cluttered very quicky.
public class PostConfiguration : EntityTypeConfiguration<Post>
{
public PostConfiguration()
{
ToTable("Posts");
HasKey(x => x.PostId)
.Property(x => x.PostId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Patient)
.WithMany()
.Map(x => x.MapKey("PatientUserId"));
HasMany(x => x.Likes)
.WithRequired(x => x.Post)
.Map(x => x.MapKey("PostId"));
}
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
ToTable("Users");
HasKey(x => x.UserId)
.Property(x => x.UserId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class LikeConfiguration : EntityTypeConfiguration<Like>
{
public LikeConfiguration()
{
ToTable("Likes");
HasKey(x => x.LikeId)
.Property(x => x.LikeId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.User)
.WithMany()
.Map(x => x.MapKey("UserId"));
}
}
// In your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.AddFromAssembly(GetType().Assembly);
}
Now when you want to query your data, EF will manage the FK relationships and resolving any Joins and resulting Cartesian products. The question just becomes what data do you want out of the domain model?
var viewData = _context.Posts
.Select(p => new PostDTO
{
ID = p.PostID,
UserName = p.Patient.UserName,
Text = p.Text,
Likes = p.Likes
.Select(l => new LikeDTO
{
ID = l.LikeId
UserName = l.User.UserName
}).ToList(),
Liked = p.Likes.Any(l => l.User.UserId == userModel.UserId)
}).OrderByDescending(p => p.Date)
.ToList();
This is a rough guess based on your original query where you want the posts, patient name, likes, and an indicator whether the current user liked the post or not. Note there are no explicit joins or even eager loading. (Include) EF will build the necessary statement with just the columns you need to populate your DTOs and the IDs necessary to relate the data.
You should also avoid mixing DTOs with Entities when returning data. In the above example I introduced a DTO for the Like as well as the Post since we want to return some detail about the Likes from our domain. We don't pass back references to Entities because when these get serialized, the serializer would try and touch each property which can cause lazy loading to get triggered and overall would return more information than our consumer needs or should potentially see. With the relationships mapped and expressed through navigation properties EF will build a query with the required joins automatically and work through the returned data to populate what you expect to see.
I don't think using TPH or TPT inheritance method influence on this.
My goal is to have a single method that load everything from the database with mixed types of entities that may have separate relationship depending on the type.
Let's take this code-first model (simplistic mock-up to represent my problem):
public abstract class Entity
{
public int ID { get; set; }
public string Name { get; set; }
}
public abstract class EntityWithInfo : Entity
{
public AdditionalInformation Info { get; set; }
}
public class DerivedEntityWithInfo : EntityWithInfo
{
}
public class DerivedEntityWithInfo2 : EntityWithInfo
{
}
public class DerivedEntityWithoutInfo : Entity
{
}
public class AdditionalInformation
{
public int ID { get; set; }
public int SomeProperty { get; set; }
}
And the Fluent API Configuration:
modelBuilder.Entity<Entity>()
.HasKey(e => e.ID)
.Map<DerivedEntityWithInfo>(m => m.Requires("Type").HasValue(1)
.Map<DerivedEntityWithInfo2>(m => m.Requires("Type").HasValue(2)
.Map<DerivedEntityWithoutInfo>(m => m.Requires("Type").HasValue(3);
modelBuilder.Entity<EntityWithInfo>()
.HasRequired(e => e.Info)
.WithRequiredPrincipal()
.Map(e => e.MapKey("Entity_FK"));
modelBuilder.Entity<AdditionalInformation>()
.HasKey(e => e.ID);
Or visually:
With the SQL schema being simple:
Table Entity with: Id, Type, Name
Table AdditionalInformation with: Id, SomeProperty, Entity_FK
Now, I want to be able to do something like:
context.Entity.Where(t => t.ID = 304 || t.ID = 512).ToList();
This gives me correctly the list of all entities, and properly typed. But, of course the Info property is always null. Disabling LazyLoading and removing virtual don't force to load it either, as I understood I absolutely need to have a .Include(t => t.Info) line in there.
I know I can call
context.Entity.OfType<EntityWithInfo>().Include(t => t.Info).Where(t => t.ID = 304 || t.ID = 512).ToList();
But then I will only get entities derived of EntityWithInfo, and not the DerivedEntityWithoutInfo ones.
So let's try with an union:
context.Entity.OfType<EntityWithInfo>().Include(t => t.Info).Cast<Entity>()
.Union(context.Entity.OfType<DerivedEntityWithoutInfo>().Cast<Entity>())
.Where(t => t.ID == 719 || t.ID == 402);
This does not work, it tells me "Entity does not declare navigation property Info".
And also, I guess this would create one hell of a SQL query.
In fact, the very reason why I'm on this is because of a very old project that was using LINQ2SQL doing the equivalent with "LoadWith" options generating an abusive SQL query (duplicate the relationship table in a join for every inherited type). Loading a single entity take more times than loading the entire table of thousands of elements raw without hierarchy. So I was trying to see if porting to EntityFramework would generate a more optimal SQL query.
So, is there a way to do this or we are simply trying to do something the wrong way? This doesn't seems to be a very popular method to have both inheritance, relationship on derived classes, and eager loading because I find pretty much no resource on that online.
At this point, any suggestion on how to create this object model from this database model would be appreciated. Thanks
This is now supported in EF Core 2.1. Now to see if the resulting query is not too much performance-hungry.
I have a database generated by application which I can't modify (I can add tables, views and such but I can't modify existing tables, add columns to them). I work on a web application which uses BreezeJS to allow the client-side part of the web app query for the data via OData protocol.
Measurement table has following structure:
MeasurementId INT
DeviceId INT FOREIGN KEY REFERENCES Devices (DeviceId)
Name VARCHAR,
PRIMARY KEY (MeasurementId)
What I need is to add nullable ParentId self referencing foreign key and because I can't modify existing tables, I've created new one, Measurement_Parent:
MeasurementId INT FOREIGN KEY REFERENCES Measurements (MeasurementId),
ParentId INT FOREIGN KEY REFERENCES Measurements (MeasurementId),
PRIMARY KEY (MeasurementId)
I have following entity:
public partial class Measurement
{
public Measurement()
{
this.Children = new List<Measurement>();
}
public Int32 MeasurementId { get; set; }
public virtual Measurement Parent { get; set; }
public Int32 DeviceId { get; set; }
public virtual Device Device { get; set; }
public String Name { get; set; }
public virtual ICollection<Measurement> Children { get; set; }
}
Now the tricky part. I've tried many different approaches to get this working but without success. Current EntityTypeConfiguration for my entity looks like this:
// Primary Key
this.HasKey(m => m.MeasurementId);
// Table & Column Mappings
this.Property(t => t.MeasurementId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Table & Column Mappings
this.ToTable("Measurement");
this.Property(m => m.MeasurementId);
this.Property(m => m.DeviceId);
this.Property(m => m.Name);
// Relationships
// Each measurement references device performing the measurement.
this.HasRequired(d => d.Device)
.WithMany(m => m.Measurements)
.HasForeignKey(d => d.DeviceId);
// Each measurement can have optional parent.
this.HasOptional(measurement => measurement.Parent)
.WithMany() // .WithMany(measurement => measurement.Children) ??
.Map(m =>
{
m.MapKey("ParentId");
m.ToTable("Measurement_Parent");
});
Unfortunately this gives me weird error while loading my app:
Metadata query failed for: api/EDW/Metadata; The specified table 'Measurement_Parent' was not found in the model. Ensure that the table name has been correctly specified.
I have no idea why is this happening because the table is there. I tried mapping these two tables onto one entity (table splitting), but because the ParentId can be NULL and EF generated INNER JOIN instead of LEFT OUTER JOIN for this mapping, it didn't work because some rows in Measurement table were ommited as they didn't have any corresponding rows in Measurement_Parent.
Basically what I need is to have optional Parent property with reference to parent measurement and list of Children measurements.
What you need is entity splitting - splitting a single entity among two or more tables. This implicitly involves shared primary key - in this case, the shared key in the relationship table will be the child entities' ID. You do this by calling multiple Map methods, each with a call to EntityMappingConfiguration.Properties to define which properties should be included in that mapping fragment and a call to ToTable to set the table name.
modelBuilder.Entity<Measurement>()
.HasKey( ke => ke.MeasurementId )
.Map( emc =>
{
emc.Properties( pe => new { pe.MeasurementId, pe.Name, pe.DeviceId } );
emc.ToTable( "Measurement" );
} )
.Map( emc =>
{
emc.Properties( pe => new { pe.MeasurementId, pe.ParentId } );
// maybe name this MeasurementExtension? This table could
// be used for any properties you wish to add to the Measurement entity
emc.ToTable( "Measurement_Parent" );
// for this example's clarity I've named the PK Child_MeasurementId
// but in real-world use I would name it MeasurementId
emc.Property( pe => pe.MeasurementId ).HasColumnName( "Child_MeasurementId" );
emc.Property( pe => pe.ParentId ).HasColumnName( "Parent_MeasurementId" );
} );
modelBuilder.Entity<Measurement>()
.HasOptional( npe => npe.Parent )
.WithMany( npe => npe.Children )
.HasForeignKey( fke => fke.ParentId );
Here's the result in the DB (note I did not set up a FK/nav prop for Device but you know how to do that):
Ideally, the Parent_MeasurementId field would be not null and the record would be deleted instead of setting that column to null if their is no parent, but that doesn't seem possible with entity splitting. In any case, this does exactly what you're looking for - extending an entity without modifying the initial underlying table.
When I am trying to clear a collection (calling .Clear) I get the following exception:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
The inner exception is:
A relationship from the 'User_Availability' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'User_Availability_Target' must also in the 'Deleted' state.
User looks like this:
....
ICollection<Availability> Availability { get; set; }
Availability looks like this:
int ID { get; set; }
User User { get; set; }
DateTime Start { get; set;
DateTime End { get; set; }
Configuration is as follows:
HasMany(x => x.Availability).WithRequired(x => x.User);
HasRequired(x => x.User).WithMany(x => x.Availability);
The code causing the problem is:
user.Availability.Clear();
I've looked at other alternatives such as using the DbSet to remove items, but I don't feel my code will be as clean. Is there a way to accomplish this by clearing the collection?
The only way that I'm aware of to make it work is defining the relationship as an identifying relationship. It would required to introduce the foreign key from Availability to User as a foreign key into your model...
public int ID { get; set; }
public int UserID { get; set; }
public User User { get; set; }
...and make it part of the primary key:
modelBuilder.Entity<Availability>()
.HasKey(a => new { a.ID, a.UserID });
You can extend your mapping to include this foreign key (just to be explicit, it isn't required because EF will recognize it by convention):
modelBuilder.Entity<Availability>()
.HasRequired(a => a.User)
.WithMany(u => u.Availability)
.HasForeignKey(a => a.UserID);
(BTW: You need to configure the relationship only from one side. It is not required to have both these mappings in your question.)
Now you can clear the collection with user.Availability.Clear(); and the Availability entities will be deleted from the database.
There is one trick. You can delete entities without using special DbSet:
(this.dataContext as IObjectContextAdapter).ObjectContext.DeleteObject(entity);
Execute this for each item in Availability collection before clearing it. You don't need 'identifying relationships' for this way.
In case someone has the same problem using SQLite:
Unfortunately the accepted answer does not work with SQLite because SQLite does not support auto increment for composite keys.
You can also override the SaveChanges() Method in the Database context to delete the children:
//// Long Version
//var localChilds = this.SubCategories.Local.ToList();
//var deletedChilds = localChilds.Where(w => w.Category == null).ToList();
//foreach(var child in deletedChilds) {
// this.SubCategories.Remove(child);
//}
// Short in LINQ
this.SubCategories.Local
.Where(w => w.Category == null).ToList()
.ForEach(fe => this.SubCategories.Remove(fe));
#endregion
See this great Blogpost as my source (Unfortunately written in german).
I am using Fluent NHibernate 1.2 for NHibernate 3.1. I have a class:
public class Marks
{
public virtual int Id { get; set; }
public virtual IList<string> Answers { get; set; }
}
In the mapping for the Marks class, I have:
HasMany(m => m.Answers).Element("Value");
When the tables are created, an "Answers" table get created with the following columns:
Marks_id (FK, int, not null)
Value (nvarchar(255), null)
What I would like to do is have the Value be nvarchar(max). I'd prefer not doing this for every string in every class, just for this one class.
I have looked at these posts: first, second, third, but haven't found anything yet that helps.
Thanks in advance for any help you can offer. If you need additional information, please let me know.
Edit:
This is the code that resolves the issue:
HasMany(x => x.Answers).Element("Value", x => x.Columns.Single().Length = 4001);
You can force mapping string to longer column at each column level in mapping either by using CustomSqlType("nvarchar(max)") or, bit more universally, by setting Length(4001) (SQL Server magic number, above which it creates nvarchar(max) automatically).
To apply it automatically for all string columns in your entities, you can write your own FluentNHibernate convention:
public class LongStringConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Type == typeof(string));
}
public void Apply(IPropertyInstance instance)
{
instance.Length(4001);
}
}
And register it in mapping container i.e. like that:
Fluently.Configure()
.Mappings(...)
.Conventions.Add<LongStringConvention>()
To apply it for collection of strings, you can use custom element mappings:
HasMany(x => x.Answers).Element("Value", x => x.Columns.Single().Length = 4001);
Came across this issue myself and the above answers were most helpful in pointing me in the right direction...but I'm using a slightly newer version of FluentNH.
For Fluent NHibernate 1.3 and NH 3.3.1, the correct code is:
HasMany(x => x.Answers).Element("Value", x => x.Length(4001));
The answers above only work for older version of nhibernate.
If you try HasMany(x => x.Answers).Element("Value", x => x.Length(4001)); you will get the following:
Error Property or indexer
'FluentNHibernate.MappingModel.ColumnMapping.Length' cannot be
assigned to -- it is read only
The correct way to do this now is (NHibernate 4.0.2.4, FluentNHibernate 2.0.1.0):
HasMany(m => m.Answers).Element("Value", e => e.Length(4001))