Property is not supported in LINQ-to-entities - c#

I'm having a problem and I don't know how to solve it. I already watched millions of posts like this but doesn't help me.
I have this:
public interface IInterface
{
int Order { get; set; }
}
public abstract class AbstractClass { }
and two implementations
public class FirstImplementation : AbstractClass, IInterface
{
[Column(nameof(IInterface.Order))]
public int Order { get; set; }
}
public class SecondImplementation : AbstractClass, IInterface
{
[Column(nameof(IInterface.Order))]
public int Order { get; set; }
}
and other implementations, but they don't have Order property.
public class MyContext
{
public DbSet<AbstractClass> AbstratClass { get; set; }
}
I cannot put property Order in AbstractClass,cause other logic and because other implementations don't have the property, and I try these solution with ColumnAttribute to map it in one column.
But when I try these query, it throws an exception:
System.NotSupportedException: 'The specified type member 'Order' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'
The query:
myContext.AbstractClass
.OfType<FirstImplementation>()
.Concat<IInterface>(myContext.AbstractClass
.OfType<SecondImplementation>())
.Max(x => x.Order);
The goal should be to execute Max with IQueryable<T>
I tried some solutions in stackoverflow but didn't work
EDIT:
Working query:
myContext.AbstractClass.OfType<FirstImplementation>().Max(x => x.Order)
or
myContext.AbstractClass.OfType<FirstImplementation>().AsQueryable<IInterface>().Max(x => x.Order)
Exception throw when I Concat IQueryable

Your context only contains a DbSet of AbstractClass, but not of FirstImplementation. According to this OfType<DerivedType> only works with entity framework, if DerivedType is known to the DbContext (see first comment of smitpatel).

Related

Entity Framework Context.Set<T> throwing error "Entity type 'ClassName' is not part of the model for the current context"

I have 2 classes that represents the same table in the Database, Difference is in the properties of these classes. I am using this way because of differences in the custom fields in databases the application will connect to.
[Table("T1")]
public class Class1
{
.
.
}
[Table("T1")]
public class Class2
{
.
.
}
Here Now i am using the set method to get data. I have not declared anything in the DB context.
DbSet<Class1> set = context.Set<Class1>();
return set.Where(p => p.Active == "Y");
But system throws error
'Entity type 'Class1' is not part of the model for the current context'
What i am trying to achieve is to define the model for the DbSet dynamically, is this possible in some other way?
To use the DbContext.Set<T>() method, your context needs to have a matching DbSet<T> property. If you have a setup where one of the tables changes, then you have a couple of options.
Add both DbSet<Class1> and DbSet<Class2> properties to your DbContext:
public DbSet<Class1> Class1s { get; set; }
public DbSet<Class2> Class2s { get; set; }
I'm personally not a fan of doing this.
Have a generic context and make the table that changes inherit from a common base. For example:
public class BaseClass
{
//shared properties go in here
}
public class Class1 : BaseClass
{
//custom properties here
}
public class Class2 : BaseClass
{
//custom properties here
}
And your context could look like this:
public class MyContext<T> : DbContext
where T : BaseClass
{
public DbSet<T> Class1s { get; set; }
}

Casting of self-referencing entities, detect generic type

Once, I got an interface for all entities:
public interface IEntity
{
int Id { get; set; }
}
For some entities a mutationtable will exist, to log, what was done for which entity (CRUD)
public interface IMutation<TEntity> where TEntity : IEntity
{
ICollection<Mutation<TEntity>> Mutations { get; set; }
}
For each entity that implements IMutation Entity Framework will create a table with name Mutation<EntityName>
So, Mutation<EntityName> is an entity, too.
public class Mutation<TEntity> : Entity where TEntity : IEntity
{
public int EntityId { get; set; }
public TEntity Entity { get; set; }
}
I implemented the interface IEntity on a class, that some entities will inherit.
public class Entity : IEntity
{
public int Id { get; set; }
}
The entity Test inherits from Entity (becuase it's an entity) and implements IMutation with a reference to itself
public class Test : Entity, IMutation<Test>
{
public ICollection<Mutation<Test>> Mutations { get; set; } = new List<Mutation<Test>>();
}
Entity Framework gets it, and creates the two tables:
Test with properties Id and Name
Mutation<Test> with property Id (the PK from IEntity) and EntityId (the FK referencing the Test-entity)
this all works great. DB-schema and so on.
So what I want to do is, always, when one entity taht implements IMutation<EntityName> is changed, a new dataset shall be created.
There is the possibility to override SaveChanges of DbContext. Nice, so I tried it:
public override int SaveChanges()
{
IEnumerable<EntityEntry> entries = ChangeTracker.Entries(); // gets me all entries that were changed
IEnumerable<IEntity> mutationEntries =
entries.Select(s => s.Entity).Where(
w =>
w.GetType()
.GetInterfaces()
.Any(
x =>
x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(IMutation<>)))
.Select(s => (IEntity)s);
// so here now I got the entries that implement IMutation<?> <-- call this now ?-type
// what I'd now want to do is:
foreach(var entry in mutationEntries)
{
IMutation<?> mutationEntry = (IMutation<?>)entry;
mutationEntry.Mutations.Add(new Mutation<?>{ /* later on, add here CRUD, Id, user who changed,... */ });
}
return base.SaveChanges();
}
The problem now is, that I never know, what my ?-Type is. I know it has to be from Type IEntity.
But when I try to parse the Entity to IMutation<IEntity> i get an error, saying, he cannot cast from IMutation<Test> to IMutation<IEntity>. (But Test implements IEntity)
Tried it this way:
IEnumerable<IMutation<IEntity>> mutationEntries =
entries.Select(s => s.Entity).Where(
w =>
w.GetType()
.GetInterfaces()
.Any(
x =>
x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(IMutation<>)))
.Select(s => (IMutation<IEntity>)s);
But I'm already checking, whether my Entity implements IMutation.
Maybe someone has an idea, how I could solve this issue?
It's hard to work with generic interfaces that are not covariant and have no non generic counterparts (like IEnumerable<T> -> IEnumerable, IQueryable<T> -> IQueryable etc.).
The only remaining choice in such case is reflection or dynamic dispatch.
For instance, you could add a method like this:
private void ProcessMutationEntity<TEntity>(TEntity entity)
where TEntity : IEntity, IMutation<TEntity>
{
entity.Mutations.Add(new Mutation<TEntity> { EntityId = entity.Id, Entity = entity});
}
and then use DLR to call it (using the code from the first example):
// ...
foreach (var entry in mutationEntries)
{
ProcessMutationEntity((dynamic)entry);
}
// ...

Cannot wrap entity with interface because IDbSet interface is not covariant

I'm using entity framework database first and I faced a problem that I cannot solve elegantly. Let's say in my autogenerated entity I have some proprties that I don't want to use (or I don't want to allow somebody to use them). So I came up with a good idea to write an interface which provide only a part of properties from the entity and I would like to use interface reference rather than exact entity class. For example let's say that I have an Entity table in database and this is the generated code and my interface (I don't want to even know about RedundantString column in database):
public interface IEntity
{
int Id { get; }
Type NavigationProperty { get; }
}
internal class Entity : IEntity
{
public int Id { get; set; }
public string RedundantString { get; set; }
public virtual Type NavigationProperty { get; set; }
}
Everything is ok, however there is one problem. In my DbContext I have a property:
public virtual DbSet<Entity> Entities { get; set; }
As I said I do not want to have direct access to Entity type, I would rather use IEntity. So I want to pack my context in antother interface that would provide only property:
IDbSet<IEntity> Entities { get; }
Here is the problem. I can't implement that property using IDbSet<Entity> because IDbSet is not declared to be covariant, so common cast:
IDbSet<IEntity> IMyDbContext.Entities
{
get { return (IDbSet<IEntity>)Entities; }
}
is compiling fine but throws a runtime exception.
What can I do to solve that problem?

Mapping Id attribute by Reflection

I'm trying to map the Id attribute from my entity classes with reflection using FluentNHibernate.
My entities:
public abstract class BaseEntity
{
public int Id { get; set; }
}
public class Entity : BaseEntity
{
public string Name { get; set; }
}
Ok, my mapping class is like above:
public class BaseMapping<E> : ClassMap<E>
{
public BaseMapping(string schema, string table)
{
Schema(schema);
Table(table);
Id(model => typeof(E).GetProperty("Id", typeof(int)), "Id")
.GeneratedBy.Identity()
.Not.Nullable();
}
}
public class EntityMapping : BaseMapping<Entity>
{
public EntityMapping() : base("dbo", "Entities")
{
Map(model => model.Name, "Name")
.Length(50)
.Insert().Update()
.Not.Nullable();
}
}
I am receiving this exception:
{"Identity type must be integral (int, long, uint, ulong)"}
When I map the Id attribute on the EntityMapping class...
Id(model => model.Id, "Id")
.GeneratedBy.Identity()
.Not.Nullable();
It's works like a charm. But the first attempt is not working.
Firstly your properties should be marked as virtual for your entities. This is so the NHibernate framework can perform its magic lazy loading voodoo.
That being said. Lets assume all your entities derive from BaseEntity as it appears. Because of this assumption you can let the typeparam E understand that it will always be a BaseEntity.
Once you do this you can then rewrite the BaseMapping<E> method as such.
public class BaseMapping<E> : ClassMap<E>
where E: BaseEntity
{
public BaseMapping(string schema, string table)
{
Schema(schema);
Table(table);
Id(model => model.Id, "Id");
}
}
By specifing where E: BaseEntity will expose the properties of E to your method. I have only tested this code up to the point of the mapping methods completing for multiple entity types.
As for why you recieved your message the statement
typeof(E).GetProperty("Id", typeof(int))
returns a type of PropertyInfo where you need to pass the member expression for the memberExpression parameter. By digging through the source of FluentNHibernate they use the Expression to evaluate to a Member through reflection.

Entity Framework and generics

I have a couple independent objects, each of which has a list of a common object. For instance,
public class Project
{
public IEnumerable<CommentEntry<Project>> Comments{get;set;}
}
public class Sample
{
public IEnumerable<CommentEntry<Sample>> Comments{get;set;}
}
public class CommentEntry<T> where T: class
{
public int TId {get;set;}
public int CommentEntryId{get;set;}
public DateTime TimeStamp{get;set;}
public string Comment{get;set;}
}
Using fluent api of Entity Framework 5, I would like a CommentEntry table for Projects and Requests. So, here is my mapping code:
modelBuilder.Entity<CommentEntry<Project>>()
.Map(m =>
{
m.ToTable("EngineeringProjectComments");
});
modelBuilder.Entity<CommentEntry<Request>>()
.Map(m =>
{
m.ToTable("SampleRequestComments");
});
When I attempt my migration I encounter the following message:
The type CommentEntry`1[Project]' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.
I can see the obvious flaw of my attempt to use generics in this context. However, can anyone suggest an alternative to my database table structure, classes code or mapping code that will allow me to share the single, generic type among many classes and have independent tables?
Just use the normal inheritance structure. And, instead of using a specific ID name, like EngineeringProjectId, just use Id.
public class Project
{
public ICollection<ProjectCommentEntry> Comments{get;set;}
}
public class Sample
{
public ICollection<SampleCommentEntry> Comments{get;set;}
}
public class ProjectCommentEntry : CommentEntry {}
public class SampleCommentEntry : CommentEntry {}
public class CommentEntry
{
public int Id {get;set;}
public int CommentEntryId{get;set;}
public DateTime TimeStamp{get;set;}
public string Comment{get;set;}
}
By the way, you can't use IEnumerable for navigation properties in EF, you need a full collection which is why you should use ICollection instead.

Categories

Resources