EF CTP5 POCO, base class and how to implement inheritance - entity-framework

Hey,
lets say I want all my recordsto have a standard stamp on them.
inserted_by, inserted_at, updated_by, updated_at, deleted_by, deleted_at, timestamp
1 - If I had to put this in a base (maybe abstract) POCO class, what would be the best inheritance strategy to implement this. (I am using GUID as primary keys.)
I do not want to use base class for anything else.
In my Db Context;
I'd like to use the end POCO classes that corresponds to the db table.
DbSet, looks like I have to use DbSet tough, then use OfType to query:)
2 - If Inheritance is out of context, what would you recommend, ComplexType, an Interface maybe?

I do exactly that in EF4. There is a generic repository base class:
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
All entity repositories inherit from this class. The generic .Add() and .Update() method automatically set the audit data:
public void Add(T entity)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = UserName;
entity.LastModifiedOn = entity.CreatedOn;
entity.LastModifiedBy = entity.CreatedBy;
ObjectContext.AddObject(GetEntitySetName<T>(), entity);
}
public void Update(T entity)
{
T originalEntity = ObjectSet.Single(t => t.Id == entity.Id);
entity.CreatedOn = originalEntity.CreatedOn;
entity.CreatedBy = originalEntity.CreatedBy;
entity.LastModifiedOn = DateTime.UtcNow;
entity.LastModifiedBy = UserName;
ObjectSet.ApplyCurrentValues(entity);
}
So you can see that it doesnt go into the POCO base class BaseEntity, because it's not the responsibility of the POCO. Instead it belongs to the Repository.

You need TPC inheritance (Table per class or Table per concrete type). Check this article about CTP5 mapping of TPC.

Related

Entity Framework Set with Generic Class

Ok, I might be punching above my pay grade here, but I'm trying to create a generic CRUD routine for and EF project. I've got most of it working but I'm flailing around on one point.
Normally you do something like this to add an entity through a context-
DBContext.MyClass.Add( p ); // p = instance of MyClass
That works fine, but since in a global method to handle all adds regardless of what class they are I'm passing in a Model as an object it would look more like this-
DBContext<whateverobject>.Add(whateverobject); // my objects is an object param passed into the method
I've tried doing a bunch of typeofs and there where T : class stuff but I'm having no luck. Any pointing in the right direction would help me out.
I'm using EF Core 2 so my options might also be more limited than EF 6.
Thanks.
The method you're looking for is DbContext's Set<T>()
Your generic repository for your generic CRUD would look something like this:
public class Repo<T> where T: class
{
private readonly DbSet<T> _set;
public Repo(DbContext dbContext)
{
_set = dbContext.Set<T>();
}
public void Add(T entity) => _set.Add(entity);
}
This example includes a maybe unusual thing:
where T: class: we have to specify that T has to be a reference type because DbSet<T> expects T to be a reference type
For generic querying you might want to use extension methods.
In order to implement a ById method you'd have to specify that the type T must have an Id property using an interface. That would look something like this:
public interface IEntity
{
int Id { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
}
public static class DbSetExtensions
{
public static T ById<T>(this DbSet<T> dbSet, int id) where T: class =>
dbSet.FirstOrDefault(entity => entity.Id == id);
}

JPA Generic Entity using Map

Is there a way to implement JPA Entity using Map? either extended HashMap or contain a hashmap i.e.:
#Entity
#Table(employee)
public class Employee {
/* .... */
void set(String columnName, Object columnValue) { /*...*/ }
Object get(String columnName) { /*...*/ }
}
and
#RepositoryRestResource
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
this way, there is no need to provide model attributes in the Employee class. Basically, whatever columns are defined in the database, it will be a property like entry in the Employee class.
Employee emp;
...
emp.get("name");
emp.get("id");
Is something like this possible?
Though not possible in vanilla JPA, EclipseLink dynamic entities (https://wiki.eclipse.org/EclipseLink/Examples/JPA/Dynamic) might be what you want. It allows for working with entities in a map-like fashion. Not sure if it is sufficient for your use case, though.

Entity framework code first creates "discriminator" column

I am using EF CF approach for a website with MySQL.
For some reason EF creates a column in my Post table called "Discriminator" and contains the VARCHAR "Post".
Why is this column created? Can I do something to avoid it being created? Are there any advantages of having this column?
The Discriminator column is used and required in Table-Per-Hierarchy inheritance scenarios. If you for example have a model like this ...
public abstract class BaseEntity
{
public int Id { get; set; }
//...
}
public class Post : BaseEntity
{
//...
}
public class OtherEntity : BaseEntity
{
//...
}
... and make the BaseEntity part of the model, for instance by adding a DbSet<BaseEntity> to your derived context, Entity Framework will map this class hierarchy by default into a single table, but introduce a special column - the Discriminator - to distinguish between the different types (Post or OtherEntity) stored in this table. This column gets populated with the name of the type (again Post or OtherEntity).
You can stop the column being created by adding the [NotMapped] data annotation to the models that are inheriting from your base class. This will tell EF not to add your class to future migrations, removing the discriminator column.
public class BaseClass
{
}
[NotMapped]
public class InheritingClass : BaseClass
{
}
For completeness, if you want to use the fluent API to stop the inheriting class from being mapped with entity (and therefore stopping the discriminator column being created) you can do the following:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<InheritingClass>();
}

Update method for generic Entity framework repository

I have a repository like that:
public class Repository<T> : IRepository<T> where T : class
{
private readonly IRepositoryContext _repositoryContext;
public Repository(IRepositoryContext repositoryContext)
{
_repositoryContext = repositoryContext;
_objectSet = repositoryContext.GetObjectSet<T>();
}
public virtual void Update(T entity)
{
ObjectSet.AddObject(entity);
_repositoryContext.ObjectContext.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
_repositoryContext.SaveChanges();
}
}
Now that actually works for all scalar properties of the entity, but all the other entities that associated with properties of entity typeOf(T), don't care that entity state is modified, and EF simply adds new data.
So, if you do for example Repository<Student>.Update(), and you only changed the name, it will find the right Student and change his name, but it also will change the Campus, although you already have a Campus associated with that student, it will be created again with a different CampusId.
Show me please the correct way to do updates in this situation.
What I did when I wanted to follow generic approach was translated to your code something like:
public class Repository<T> : IRepository<T> where T : class
{
...
public virtual void Update(T entity)
{
if (context.ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Detached)
{
throw new InvalidOperationException(...);
}
_repositoryContext.SaveChanges();
}
}
All my code then worked like:
var attachedEntity = repository.Find(someId);
// Merge all changes into attached entity here
repository.Update(attachedEntity);
=> Doing this in generic way moves a lot of logic into your upper layer. There is no better way how to save big detached object graphs (especially when many-to-many relations are involved and deleting of relations is involved).

Entity Framework, LINQ and Generics

I have the following code:
public interface IKeyed<TKey>
{
TKey Id { get; }
}
// This is the entity framework generated model. I have added the
// IKeyed<Guid> interface
public partial class Person : IKeyed<Guid>
{
public Guid Id { get; set; }
}
public class Repository<TKey, TEntity> : IKeyedRepository<TKey, TEntity>
where TEntity : class, IKeyed<TKey>
{
private readonly IObjectSet<TEntity> _objectSet;
public Repository(IOjectSet<TEntity> objectSet)
{
_objectSet = objectSet;
}
public TEntity FindBy(TKey id)
{
return _objectSet.FirstOrDefault(x => x.Id.Equals(id));
}
}
[Update]
Here is how I am calling this:
Db2Entities context = new Db2Entities(_connectionString); // This is the EF context
IObjectSet<Person> objectSet = context.CreateObjectSet<Person>();
IKeyedRepository<Guid, Person> repo = new Repository<Guid, Person>(objectSet);
Guid id = Guid.NewGuid();
Person person = repo.FindBy(id); // This throws the exception.
The above code compiles. When the 'FindBy' method is executed, I get the following error:
Unable to create a constant value of type 'Closure type'. Only primitive types (for instance Int32, String and Guid) are supported in this context.
Since the type of my 'Id' is a Guid (one of the primitive types supported) it seems like I should be able to massage this into working.
Anyone know if this is possible?
Thanks,
Bob
It doesn't work this way. You cannot call Equals because EF doesn't know how to translate it to SQL. When you pass expression to FirstOrDefault it must be always only code which can be translated to SQL. It is probably possible to solve your problem with some manual building of expression tree but I can reference other solutions already discussed on Stack Overflow.
ObjectContext offers method named GetObjectByKey which is exactly what you are trying to do. The problem is that it requires EntityKey as parameter. Here are two answers which show how to use this method and how to get EntityKey:
Entity Framework Simple Generic GetByID but has differents PK Name
generic GetById for complex PK
In your case the code will be less complicated because you know the name of the key property so you generally need only something like this:
public virtual TEntity FindBy(TKey id)
{
// Build entity key
var entityKey = new EntityKey(_entitySetName, "Id", key);
// Query first current state manager and if entity is not found query database!!!
return (TEntity)Context.GetObjectByKey(entityKey);
}
The problem here is that you cannot get entitySetName from IObjectSet so you must either pass it to repository constructor or you must pass ObjectSet.
Just in case you will want to use DbContext API (EFv4.1) in the future instead of ObjectContext API it will be much simplified because DbSet offers Find method:
generic repository EF4 CTP5 getById