0

I'm having difficulty figuring out how to only map a base class in Entity Framework to my database, while preventing any information about classes that inherit it from being mapped to the database.

I created a simple example to highlight my difficulty where there is a base Worker class, and a child class called Engineer. For some reason, assume that I don't care to store details of the engineer... I only want my database to contain the information in the Worker class.

So I've done the following:

class Program
{
    static void Main(string[] args)
    {
        Engineer e = new Engineer();
        e.Name = "George";
        e.Focus = "Software";

        MyDatabase db = new MyDatabase();
        e.Save(db);
    }
}

public class MyDatabase : DbContext
{
    public DbSet<Worker> Workers { get; set; } 
    public MyDatabase() : base("mytempdb") { }
}

[NotMapped]
public class Engineer : Worker
{
    public string Focus { get; set; }
}


public class Worker
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }
    public string Name { get; set; }

    public bool Save(MyDatabase db)
    {
        try
        {
            if (db.Workers.Any(w => w.ID == this.ID))
            {
                db.Workers.Attach(this);
                db.Entry(this).State = System.Data.Entity.EntityState.Modified;
            }
            else
            {
                db.Workers.Add((Worker)this);
            }
            db.SaveChanges();
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            return false;
        }

        return true;
    }
}

I am doing "Database First" design, and my database table looks like:

CREATE TABLE [dbo].Workers
(
    [ID] INT NOT NULL PRIMARY KEY, 
    [Name] NVARCHAR(200) NULL, 
    [Discriminator] NVARCHAR(200) NULL
)

However, when I run my program, it is still looking for a entity to store Engineer in. I've told it not to map it, hoping that it would only map the base class to the database:

System.InvalidOperationException: Mapping and metadata information could not be found for EntityType 'ConsoleApplication10.Engineer'.

2 Answers 2

1

I think you might have to map the entity to a new Worker entity before saving so that entity framework doesn't look for the object's sub type—Engineer.

For example:

public class Worker
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }
    public string Name { get; set; }

    public static FromWorker(Worker worker)
    {
        // example of mapping the entity
        return new Worker
        {
            ID = worker.ID,
            Name = worker.Name,
        };
    }

    public bool Save(MyDatabase db)
    {
        try
        {
            Worker worker = Worker.FromWorker(this);

            if (db.Workers.Any(w => w.ID == worker.ID))
            {
                db.Workers.Attach(worker);
                db.Entry(worker).State = System.Data.Entity.EntityState.Modified;
            }
            else
            {
                db.Workers.Add(worker);
            }

            db.SaveChanges();
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            return false;
        }

        return true;
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your solution! I think this is a little more clean than Phil's proposed solution since I won't have to mark every inheriting class as ignored. The downside is that I have to map all of the properties and create this "cloning constructor." No problem. I'll see what other proposed solutions pop up for the next hour or so. If nothing better, I'll accept your solution!
@gnychis it's definitely not nice to have to map the properties out; however, this is essentially what happens with a ViewModel going into a Model anyways so it's not too bad. Maybe there is a better way though.
@gnychis by the way, you will still always need the [NotMapped] attribute or equivalent with fluent api so that entity framework won't throw an error.
thanks! I'll keep that in mind. One potentially issue that I've found with this is if the base class is abstract. I'm not sure what to do in that case...
I guess if it's abstract I should create a new model for it, or directly use LINQ...
0

EDIT: You already use [NotMapped]
I did not see that first time sorry. So i dont expect it to be in the model.

There is a do not map attribute for properties and equivalent fluent api option for whole classes.

[NotMapped] 
public string BlogCode  { get;set; }

and for a POCO inside the Context

public class MyDbContext : DbContext {
   protected override void OnModelCreating(DbModelBuilder modelBuilder) {

        //Tell EF not to include these in DB. Ignore these please
        // Anthing that EF picks and generates a table for that shoudl not be persisted
       // modelBuilder.Ignore<EFpleaseIgnoreMe>();
        modelBuilder.Ignore<YourType>();
    }
   }

2 Comments

Correct me if I'm wrong, but isn't modelBuilder.Ignore<Engineer>() equivalent to the NotMapped attribute he already has on Engineer?
@David, it may be equivalent yes. I havent use the attribute on class, only on properties. I actually didnt see the attribute on the class when i first looked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.