0

Is it possible to join different version of Entity Framework (Core) Entities from two applications based on EF 6 (with .NetFramework 4.0) and EF Core 5.0 (.Net Standard 2.1) with the same DataBase into one Entity Interface instead of using the actual database classes that both entity frameworks provide, using a project that contains Interfaces of the actual DataBase Entities and targets both EF 6 and EF Core 5 supported framework platforms?

<!--EntityTypes.csproj-->
...
<TargetFrameworks>netstandard2.1; net40; net45</TargetFrameworks>
....
// IEntity.cs
namespace EntityTypes
{
    public interface IEntity
    {
        long EntityID { get; set; }
        string EntityName { get; set; }
    }
}

In both .Net Core and Framework 4.0 projects, I need to somehow use syntax like

var _entitiesContext = new EntitiesContext(); 
public static List<IEntity> BuildSet<T>() where T : class
{
    return _entitiesContext.Entities.ToList().Cast<IEntity>().ToList();
}

As I've already found in this question : How to Cast DBSet to Interface in Anonymous Method - E.F But without getting into the Runtime Error

System.InvalidCastException: 'Unable to cast object of type 
'System.Data.Entity.DynamicProxies.Entity_33C84C30E063218CBB4881E395838375014F3EFBA09108F25ACF2FB2FCA1E92D' 
to type 'EntityTypes.IEntity'.'
Ivan Silkin
  • 381
  • 1
  • 7
  • 15
  • EF Old and EF Core are completely different products, not different versions. Either use EF 6 in .NET Core or EF Core in .NET Old. What does your *actual* code do though? What are the *actual* classes? If they only implemented an interface there wouldn't be any problem. – Panagiotis Kanavos Oct 14 '21 at 13:11
  • PS: The answer you link to is *really* bad. It loads an entire table into memory and then tries to work with the objects. Once again, if the entities implement the interface, there should be no problem with either ORM. You can pass the entities to any method that accepts that interface. You can access the methods directly. You can cast instances to the interface if you want. What's the point of `BuildSet` ? In fact, if you used `where T : class, IEntity` you could return the result as an `IList` without casting. But that would be no different to using eg `_context.Entities.ToList()` – Panagiotis Kanavos Oct 14 '21 at 13:17
  • @PanagiotisKanavos, the database stores compositions and their metadata, I'm working with files and I'm trying to build Xamarin Forms application with .NetStandard 2.1 that works with the same DataBase as my WPF application based on .Net Framework 4.0 (I'm having problems segregating the parts of the code). – Ivan Silkin Oct 14 '21 at 13:19
  • 1
    Are you sure that your `T` is a type that implements `IEntity`? If you add `where T : IEntity` does it still compile? – DavidG Oct 14 '21 at 13:30
  • @DavigG, no. The T type I passed as an argument didn't implement that interface, I've just fixed that and It's somehow working. I was just curious about warning "// This code was generated from a template. // Manual changes to this file may cause unexpected behavior in your application." But "Entity : IEntity" just worked fine. Thanks all. :) – Ivan Silkin Oct 14 '21 at 13:50
  • @IvanSilkin `with .NetStandard 2.1` that only works in .NET Core 3.1 and later. .NET Framework 4.0 never supported .NET Standard in any version. 4..5 only supported .NET Standard 1.0 – Panagiotis Kanavos Oct 14 '21 at 13:50
  • @IvanSilkin `curious about warning "// This code was generated from a` as that warning says, the code was generated by a tool and will be rewritten. Not `BuildSet` though, that's your own custom method. If you want to add such a method to a scaffolded DbContext use a partial class. What's the point of that method though? – Panagiotis Kanavos Oct 14 '21 at 13:53
  • @PanagiotisKanavos, the purpose of the method was to segregate the actual scaffolded database classes from their implementation for UI and middleware projects, I took it from the answer: . I'm also not sure if it is working 100% correctly. – Ivan Silkin Oct 14 '21 at 14:28
  • 1
    @IvanSilkin that's not what that question was about. If you want to decouple data and application entities you need to use *different* classes, not put an interface over the data classes. An easy way to do this is to use AutoMapper. If the mapped classes follow the naming conventions you may be able to just map one type to another based on names alone. – Panagiotis Kanavos Oct 14 '21 at 14:38

2 Answers2

1

a project that contains Interfaces of the actual DataBase Entities and targets both EF 6 and EF Core 5 supported framework platforms?

Basically, this is simple. You don't need any entity interfaces. Just define your entities in a separate assembly that you reference from both EF projects, and create an abstract class or interface that defines the repository that you can implement with either an EF Core or EF6 DbContext, eg:

public class SomeEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class AnotherEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public abstract class Repository : IDisposable
{
    public abstract IQueryable<SomeEntity> SomeEntities { get; }
    public abstract IQueryable<AnotherEntity> AnotherEntities { get; }

    public abstract void SaveChanges();
    public abstract void Dispose();

}

You'll have to add methods to get shaped data because eager loading with Include is a version-specific extension, or provide methods to pass expressions into your abstract repository and create the Include in the version-specific subclass. But basic queries with IQueryable will work fine.

David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
0

I've ended up doing the following:

//ContextTypes.csproj, IDBContext.cs
public interface IDBContext
{
    void Add<TEntity>(TEntity e) where TEntity : class;
    void Remove<TEntity>(TEntity e) where TEntity : class;
    IQueryable<Entity> GetEntities();
    int SaveChanges();
}
//ContextTypes.csproj, Entity.cs
public partial class Entity
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
//...
}

//EntitiesContext.csproj, EntitiesContext.cs: (EF 6)
using ContextTypes;
public partial class EntitiesContext : DbContext, IDBContext 
{
    //...
    public virtual DbSet<ContextTypes.Entity> Entities { get; set; }
    public IQueryable<ContextTypes.Entity> GetEntities() { return Entities; }
    public void AddEntity<TEntity>(TEntity item) where TEntity : class
    {
        if (typeof(TEntity) == typeof(Entity))
        {
            Entities.Add(item as Entity);
        }
    }
    public void RemoveEntity<TEntity>(TEntity item) where TEntity : class
    {
        if (typeof(TEntity) == typeof(Entity))
        {
            Entities.Remove(item as Entity);
        }
    }
    //...
}

//EntitiesContext.csproj, EntitiesContext.cs: (EFCore 5)
    //...
    public void AddEntity<TEntity>(TEntity item) where TEntity : class
    {
           Add(item);
    }
    public void RemoveEntity<TEntity>(TEntity item) where TEntity : class
    {
           Remove(item);
    }
    //...

I've also copied the auto-generated ADO.NET context.tt Entities from EF 6 to ContextTypes.csproj and set the original ones to "internal" classes;

Now it's possible to reuse the DataBase access code through the included external interface with the included external Entity types both in EF 6 (.Net Framework 4.0) and in EF Core projects (.Net Core 3.1 || .Net Standard 2.1).

Ivan Silkin
  • 381
  • 1
  • 7
  • 15