skip to Main Content

We have a ‘base’ DbContext that defines a object model with some entities and relationships. This DbContext maps to a sql server database. This DbContext is ‘packaged’ into a separate assembly, available for other projects (as a kind of data access layer).
We use ‘code first’ approach.

Part of base context definition could be the following:

public class Blog
{
    public int Id { get; set; }
    public DateTime CreationDate { get; set; }
    //...
}
public class DataContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    //...
}

We would like to define an extended version of base DbContext, for example adding new entities but also extending some existing entities with new properties.
So, in a new project, we reference the ‘packaged’ base context (dll reference) and starts defining new data context and new entities like so:

public class ExtendedBlog : Blog
{
    public int? NewProp { get; set; }
    //...
}
where Blog is an entity type already defined in base context

we want to ‘substitute’ base Blog type with new type, so we define the entity set in the following way:

public class ExtendedDataContext : BaseDataContext
{
    public new DbSet<ExtendedBlog> Blogs { get; set; }  //using new keyword to replace existing definition
}

We also defined a mechanism, using reflection, capable of extending the method OnModelCreating, to extend data context definition using fluent api.
We can use TPH or TPT hierarchy strategy for inheriting entity types.

Our problem:
Every base/existing entity that has a relationship with Blog, should have a property like Blog blog or ICollection blogs.
Because we define a new type to extend base type (ExtendedBlog is a new type) and because we only re-define Blogs DbSet, ‘related’ entities still reference base type and don’t ‘sees’ new properties of ExtendedBlog.
We can’t, for example, write a query that apply a filter based on new properties of ExtendedBlogType.
To correct the issue, we should also extend every entity that is in relationship with extended one; and, if entities in relationship has others relationships, the ‘override’ should expand to a lot of entities.

It seems that this is not the proper way to extend a DbContext.
Ideally, using partial classes, should accomplish our requirement because we can ‘add’ properties maintaining original type. Partial class construct can be applied only in the same project.
We also checked ‘shared projects’ approach but it is only supported by Visual Studio (no Visual Studio code) and requires sharing source code (we don’t want to share source code).

Something like extension methods, applied to properties, may solve the problem but this feature is not available; in addition, EF seems to only map properties and there is no way to map, for example, getter+setter methods some table fields.

Any suggestion?
Thanks.

2

Answers


  1. You can use generics to specify the extended class to use as your blog entities. You specify the generic type in the BaseDataContext class. It can look like this:

    public abstract class BaseDataContext<TBlog> : DbContext
        where TBlog : Blog
    {
        public DbSet<TBlog> Blogs {get; set;}
    }
    

    In your other project, you can use the ExtendedBlog class for your new/extended blog entity like this in your new data context:

    public class ExtendedDataContext : BaseDataContext<ExtendedBlog>
    {
        // property "Blogs" is available here and of type "DbSet<ExtendedBlog>"
    }
    

    Your other entities must use the generic type TBlog as well to reference the specific blog entity type. As an example you might have tags for a blog and that entity can look like this:

    public class BlogTag<TBlog>
        where TBlog : Blog
    {
        public int Id { get; set;}
        public string Name {get; set;}
        public int BlogId {get; set;} // FK
        public virtual TBlog Blog {get; set; } // navigation property
    }
    
    Login or Signup to reply.
  2. Try my Answer

     public class SchoolStaff
        {
            public JuniorStaff? juniorStaff { get; set; }
            public SeniorStaff? seniorStaff { get; set; }
            public AdministrativeStaff? administrativeStaff { get; set; }
        }
        
        public class AdministrativeStaff
        {
            public int administrativeStaffId { get; set; }
        }
        
        public class SeniorStaff
        {
            public int seniorStaffId { get; set; }
        }
        
        public class JuniorStaff
        {
            public int juniorStaffId { get; set; }
        }
        
        public class DataTableHelper
        {
            public DataTable ConvertObjectToDataTable(object obj)
            {
                DataTable dataTable = new DataTable();
        
                if (obj != null)
                {
                    Type type = obj.GetType();
                    PropertyInfo[] properties = type.GetProperties();
        
                    foreach (PropertyInfo property in properties)
                    {
                        if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
                        {
                            // For nested objects, recursively create columns and rows
                            DataTable nestedDataTable = ConvertObjectToDataTable(property.GetValue(obj));
                            dataTable.Merge(nestedDataTable);
                        }
                        else
                        {
                            dataTable.Columns.Add(property.Name, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType);
                            dataTable.Rows.Add(property.GetValue(obj));
                        }
                    }
                }
        
                return dataTable;
            }
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search