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.
Related
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).
Is it possible in AutoMapper to defer mapping some properties to derived maps? For instance consider the following classes:
public abstract class Contract
{
public int ContractNumber { get; set; }
}
public class RegularContract : Contract { }
public class SpecialContract : Contract { }
public class ContractModel
{
public int Nr { get; set; }
public bool IsSpecial { get; set; }
}
I would like to create a mapping like the following. Observe that the IsSpecial property is not mapped in the mapping of Contract but in the mappings of the derived classes RegularContract and SpecialContract instead.
public class ContractMappingProfile : Profile
{
public ContractMappingProfile()
{
CreateMap<Contract, ContractModel>()
// .ForMember(t => t.IsSpecial, c => c.Ignore()) // required for valid mapping
.ForMember(t => t.Nr, c => c.MapFrom(s => s.ContractNumber));
CreateMap<RegularContract, ContractModel>()
.IncludeBase<Contract, ContractModel>()
.ForMember(t => t.IsSpecial, c => c.UseValue(false));
CreateMap<SpecialContract, ContractModel>()
.IncludeBase<Contract, ContractModel>()
.ForMember(t => t.IsSpecial, c => c.UseValue(true));
}
}
Such a configuration is however not valid in AutoMapper because IsSpecial is not mapped for Contract. In order to make it valid, I would need to add the commented line to the mapping.
However, in such a case AutoMapper will not throw a validation error if the property is not mapped in some derived class, e.g., when I change the mapping of SpecialContract to:
CreateMap<SpecialContract, ContractModel>()
.IncludeBase<Contract, ContractModel>(); // no IsSpecial mapping
Any ideas how to solve this using AutoMapper mapping inheritance?
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);
}
// ...
First, spec. We use MVC5, .NET 4.5.1, and Entity framework 6.1.
In our MVC5 business application we have a lot of repetitive CRUD code. My job is to "automate" most of it, which means extracting it to base classes and making it reusable. Right now, I have base classes for controllers, view models and EF6 entity models.
My abstract base class that all EF6 entities inherit:
public abstract class BaseEntity<TSubclass>
where TSubclass : BaseEntity<TSubclass>
{
public abstract Expression<Func<TSubclass, object>> UpdateCriterion();
}
UpdateCriterion method is used in AddOrUpdate method of database context. I have a generic parameter for subclasses because UpdateCriterion needs to return lambda expression that uses exact subclass type, not an interface or base class. An extremely simplified subclass implementing this abstract base class would look like this:
public class Worker : BaseEntity<Worker>
{
public int ID { get; set; }
public int Name { get; set; }
public override Expression<Func<Worker, object>> UpdateCriterion()
{
return worker => worker.ID;
}
}
After that, in SaveOrUpdate action of my base controller, I would have code like this:
public ActionResult Save(TViewModel viewModel)
{
if (ModelState.IsValid)
{
var entityModel = viewModel.ConstructEntityModel();
db.Set<TEntityModel>().AddOrUpdate<TEntityModel>(entityModel.UpdateCriterion(), entityModel);
db.SaveChanges();
}
}
Thanks to that, subclasses of the base controller don't need to implement Save method themselves, as they did before. Now, all of this works, and it actually works really well despite the funky syntax (I mean, class BaseEntity<TSubclass> where TSubclass : BaseEntity<TSubclass>, seriously?).
Here comes my problem. For most of the entities field ID is the key, but for some it isn't, so I can't generalise properly with a superclass implementation. So for now, every entity subclass implements it's own UpdateCriterion. But, since for most (90%+) entities e => e.ID is the correct implementation, I have a lot of duplication. So I want to rewrite the entity base class to something like this:
public abstract class BaseEntity<TSubclass>
where TSubclass : BaseEntity<TSubclass>
{
public virtual Expression<Func<TSubclass, object>> UpdateCriterion()
{
return entity => ((dynamic)entity).ID;
}
}
The intention is to provide default implementation that uses ID as key, and allow subclasses to override it if they use a different key. I can't use an interface or a base class with ID field because not all entities have it. I thought I'd use dynamic to pull out ID field, but I get following error: Error: An expression tree may not contain a dynamic operation.
So, any idea on how to do this? Would reflection work in base UpdateCriterion?
No, you cannot use dynamic in a Linq to Entities query.
But you can build the Lambda Expression at runtime.
public virtual Expression<Func<TSubclass, object>> UpdateCriterion()
{
var param = Expression.Parameter(typeof(TSubclass));
var body = Expression.Convert(Expression.Property(param, "ID"), typeof(object));
return Expression.Lambda<Func<TSubclass, object>>(body, param);
}
If the TSubclass type does not have an ID property Expression.Property(param, "ID") will throw an exception.
Additionally you could use the MetadataWorkspace from your entity model to get the Primary Key column for TSubclass.
If you are defining the BaseEntity class, you could add a virtual read only property that returns the actual ID property. I beleive EF treats read only properties as "computed", so they are not stored to the db.
public abstract class BaseEntity<TSubclass> where TSubclass : BaseEntity<TSubclass>
{
public abstract object ID { get; }
public virtual Expression<Func<TSubclass, object>> UpdateCriterion()
{
return entity => entity.ID;
}
}
public partial class Foo : BaseEntity<Foo>
{
public Int32 FooId { get; set; }
public override object ID { get { return FooId; } }
}
Just a thought - I only tried compiling in LinqPad and checking the value of a call to UpdateCriterion. :)
Take a look at this answer. It uses reflection to get the ID Property. I think it solves your problem:
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
Then you replace your lambda expression by
return entity => GetPropValue(entity, "ID");
I've not tested, since I have no code fully working to test it. If it works please let us know.
Say I have a class something like...
public class SomeClass<T> where T : ISomeConstrainingInterface
{
public T MyPropertyOfTypeT {get;set;}
public int SomeIntProp {get;set;}
public string SomeStringProp {get;set;}
}
Where T can be a fairly small limited set (say 5 or 6 types)
What is the best and most efficient way to map this class in nHibernate ? (using fluentNHibernate)
this will create a seperate table for each type concreate of SomeClass, so there is no problem with different Id types and foreign keys are possible. SomeClass should implement an interface so all can be used be queried and handled generically.
public abstract class SomeClassMapBase<T> : ClassMap<SomeClass<T>>
{
public SomeClassMapBase()
{
Map(x => x.SomeIntProp);
Map(x => x.SomeStringProp);
}
}
public class SomeClassReferencedClassMap : SomeClassMapBase<ReferencedClass>
{
public SomeClassReferencedClassMap()
{
CompositeId()
.KeyReference(x => x.Referenced, "Refernece_id");
}
}