Is Entity Frameworks Core + fluent superior to ADO.NET?

David Thielen 3,211 Reputation points
2023-03-11T19:34:28.7333333+00:00

Hi all;

I've been fighting Entity Frameworks Core + fluent for about a month now on a new project working to create my model, etc. I have a couple of decades experience using ADO.NET.

So I'm comparing the two. Granted, the first project is going to be slow as I have to learn this new API. But with the experience so far this is what I've found...

Creating the model

With EF I create the classes for each table. With ADO I would also have to create the schema in SSMS. So figure a 5 minute/table win for EF here.

Relationships & Default Values

With EF it's writing a method call for each. With ADO it's a setting in the schema. Both talk the same amount of time are are equally easy so call this a draw. (I am assuming that with time I'll have the fluent calls memorized making it as easy as SSRS.)

Read Only Properties in Model Classes

With ADO my classes would have only a getter on the properties that cannot be changed such as the primary key, creation date, etc. EF can't do this. So with EF there's the danger of changing a value in a row that should not be changed. Advantage ADO.

Per Jack's reply below, this functionality is supported in EF.

Only Insert Allowed Values

Again the Created column value in a table. With ADO this can get a default of GetUtcDate() and on an insert that column is not set. Advantage ADO.

Read and/or Upsert a Row

The is where EF shines, it does a great job of abstracting the repository away and it's just a read, access properties, update. With ADO this needs to be written as methods for each table. So figure another 5 minute/table win for EF here.

Documentation

This is where ADO shines. Granted it's because it has been around forever but ADO is very well documented and it's easy to find what you need. EF on the other hand is poorly documented, a lot of what you need is searching Stack Overflow, Microsoft Q&A, etc. and hoping the answers there are the correct/best approach.

And adding to that EF6 and EF Core have slightly different APIs and therefore you may be trying to use a solution that just doesn't work on EF Core. So giant advantage ADO here.

Non-nullable Property Warning

I personally find the new C# non-nullable warnings very useful. But with EF you must have a setter on all properties for it to create objects, but you don't assign anything to them. Therefore every model class lights up with non-nullable warnings. It's like Penny's check engine warning light in TBBT, it's of no use if you legitimately ignore it in lots of cases. So giant advantage ADO here if you use this warning.

Revising the Schema

Similar to creating the model, this requires just a change in the model class in EF while you also need to update the schema and your access methods in ADO. Adding/removing a single property that's a couple of extra minutes, but those can add up. In my case changes are rare so not much of a hit but if someone is revising their model a lot, this adds up. So advantage EF.

Saving the Schema in Git

By definition checking in your code to Git saves the database schema. With ADO you need to fire up SSRS, save of a .sql file of the schema and update that in your source files so it gets updated. The time hit here isn't big, but it's easy to forget to do this and that's big. So major advantage to EF here.

Conclusion

As I said, I'm new to EF. So I may not be seeing some of the advantages yet. And I will stick with EF for this project. But from what I've seen so far, I think ADO is still superior to EF.

With time they'll improve/complete the documentation for EF and that will do a lot to equalize them. But until they address the following 2 issues I think ADO will remain superior:

  1. Read-only properties in a model class. To enforce no setting on an insert and no changing on an update.
  2. Eliminate the non-nullable warning (and likely soon to be error) for EF model class properties.

Thoughts? Did I miss anything? Are there advantages I haven't seen yet?

Developer technologies | .NET | Entity Framework Core
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Jack J Jun 25,296 Reputation points
    2023-03-13T07:10:20.88+00:00

    @David Thielen, Welcome to Microsoft Q&A,

    Read-only properties in a model class. To enforce no setting on an insert and no changing on an update.

    For your first question, I recommend that you could use IProperty.AfterSaveBehavior Property and IProperty.BeforeSaveBehavior Property to the read-only property.

    Here is a code example you could refer to.

    #nullable disable
        public class Student
        {
            public int Id { get; set; } 
    
            public string Name { get; set; }    
    
            public string Description { get; set; }
        }
        public class StuContext:DbContext
        {
            public DbSet<Student> Students { get;set;}
    
    
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                string str = "connstr";
                optionsBuilder.UseSqlServer(str);
            }
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Student>().Property(m => m.Name).HasDefaultValue("defaultname").Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
                modelBuilder.Entity<Student>().Property(m => m.Name).HasDefaultValue("defaultname").Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
            }
        }
    
    

    After code first, if we want to insert a record with Name or change name from one record, it will not change the name.

    Insert data

    StuContext context = new StuContext();
                Student student = new Student();
                //var student = (from m in context.Students
                //               where m.Name == "defaultname"
                //               select m).FirstOrDefault();
                student.Name = "test1";
                student.Description = "d3";
                context.Students.Add(student);
                context.SaveChanges();
    
    

    Update data:

                StuContext context = new StuContext();
      
                var student = (from m in context.Students
                               where m.Name == "defaultname"
                               select m).FirstOrDefault();
    
                student.Name = "test1";
    
                context.SaveChanges();
    

    We can find that the database data could not be changed and only could be set to be default value.

    User's image

    Eliminate the non-nullable warning (and likely soon to be error) for EF model class properties.

    For your second question, as you mentioned, it may be the best way to use #nullable disable for ef classes.

    Best Regards,

    Jack


    If the answer is the right solution, please click "Accept Answer" and upvote it.If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Karen Payne MVP 35,586 Reputation points Volunteer Moderator
    2023-03-13T18:46:31.49+00:00

    As you have found out each have advantages and disadvantages. Personally I use EF Core as a primary method to work with data and secondary using a data provider coupled with Dapper. See my simple Dapper code sample.

    Creating the model/Revising the Schema

    Have you seen EF Power Tools free Visual Studio extension? EF Power Tools tutorial

    • Create the database and table and relationships, for SQL-Server in SSMS
    • Create a project in Visual Studio
    • Run EF Power Tools
    • Make changes to the schema in SSMS
    • Run EF Power Tools again to pickup the changes

    Only Insert Allowed Values

    Consider Data Annotations or Fluent validation or more advance, EF Core Interceptors.

    Interceptor example

    namespace NorthWindSqlServerLibrary.Interceptors
    {
        /// <summary>
        /// Objective here is to assert for Region property equal to specific value
        /// and if so reject the save.
        /// </summary>
        public class LoggingSavingChangesInterceptor : SaveChangesInterceptor
        {
            public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
                DbContextEventData eventData, 
                InterceptionResult<int> result, 
                CancellationToken cancellationToken = new())
            {
                if (InspectCustomerRegion(eventData.Context.ChangeTracker.Entries()))
                {
                    result = InterceptionResult<int>.SuppressWithResult(0);
                }
    
                return new ValueTask<InterceptionResult<int>>(result);
            }
    
            /// <summary>
            /// Provides a way to inspect model/entry values
            /// </summary>
            /// <param name="entries"></param>
            /// <returns></returns>
            private static bool InspectCustomerRegion(IEnumerable<EntityEntry> entries)
            {
                foreach (EntityEntry entry in entries)
                {
                    if (entry.State != EntityState.Modified) continue;
                    if (entry.Entity is not Customers customer) continue;
                    if (customer.Region == "Canada")
                    {
                        return true; // reject this entities region
                    }
    
                }
    
                return false;
            }
        }
    }
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.