user1576474
user1576474

Reputation: 674

Entity Framework CodeFirst strange NullReference

I have a strange nullreference with adding data to my DB. I have three key reference in one model. Two key work perfectly but last ... For example my simple code-first models:

   [Table("Users")]
public partial class User
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ClientId { get; set; }
    [StringLength(60)]
    public string ClientType { get; set; }
    [StringLength(160)]
    public string ClientName { get; set; }
    public virtual ICollection<Repair> Repairs { get; set; }
    public User()
    {
        Repairs = new List<Repair>();
    }
}
[Table("Engineers")]
public partial class Engineer
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [StringLength(70)]
    public string Name { get; set; }

    public virtual ICollection<Repair> Repairs { get; set; }
    public Engineer()
    {
        Repairs = new List<Repair>();
    }
}
[Table("CurrentStatuses")]
public partial class CurrentStatus
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int StatusId { get; set; }

    [StringLength(60)]
    public string Status { get; set; }

    public virtual ICollection<Repair> Repairs { get; set; }
    public CurrentStatus()
    {
        Repairs = new List<Repair>();
    }
}

Some data was added to Users, Engineers and CurrentStatuses. In SQL Management Studio all is OK! This tables is stupidy simple. Entity framework can get data from this tables, store it at DataGrid, ComboBox and etc... Next main table code-first model:

    [Table("Repairs")]
public partial class Repair
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    [Column(TypeName = "date")]
    public DateTime Date { get; set; }
    [StringLength(255)]
    public string HardwareInfo { get; set; }
    public virtual User User { get; set; }
    public virtual Engineer Engineer { get; set; }
    public virtual CurrentStatus CurrentStatus { get; set; }
    [StringLength(140)]
    public string Info { get; set; }

}

I want to add new "Repair": 1) Fetch Engineers:

        public static async Task<ObservableCollection<Engineer>> GetAllEngineers()
    {
        using (var cntx = new ServiceDBContext())
        {
            ObservableCollection<Engineer> tmp = new ObservableCollection<Engineer>();
            await (cntx.Engineers.Include(xx => xx.Repairs).ForEachAsync(new Action<object>((object obj) => { tmp.Add((Engineer)obj); })));
            return tmp;

        }
    }

2) Fetch Users (sorry for dummy code): public static async Task> GetAllUsers() {

        using (var cntx = new ServiceDBContext())
        {
            ObservableCollection<User> tmp = new ObservableCollection<User>();
            List<User> users = await cntx.Users.ToListAsync();
            foreach (var itm in users)
            {
                tmp.Add(itm);
            }

            return tmp;
        }
    }

3) Fetch CurrentStatuses:

 internal static async Task<ObservableCollection<CurrentStatus>> GetAllCurrentStatuses()
    {
        using (var cntx = new ServiceDBContext())
        {
            ObservableCollection<CurrentStatus> tmp = new ObservableCollection<CurrentStatus>();
            await(cntx.CurrentStatuses.ForEachAsync(new Action<object>((object obj) => { tmp.Add((CurrentStatus)obj); })));
            return tmp;

        }
    }

4) Ok! I have all data to write new "Repair" (All type is OK).

        private void _addRepair()
    {

        FastRepair.Id =  2;
        FastRepair.Date = SelectedDate;
        FastRepair.User = SelectedUser;
        ...
        FastRepair.CurrentStatus = SelectedStatus

        using (ServiceDBContext cntx = new ServiceDBContext())
        {
            cntx.Users.Attach(FastRepair.User);
            cntx.Engineers.Attach(FastRepair.Engineer);
            cntx.CurrentStatuses.Attach(FastRepair.CurrentStatus);

            cntx.Repairs.Attach(FastRepair);
            cntx.Entry(FastRepair).State = EntityState.Added;

            cntx.SaveChanges();
        }

And i have NullReferenceException when want fetch FastRepair.CurrentStatus again, in Database all three column have digits, database have keys to tables. SaveChanges() succesfully add all data. All is ok, but Entity throw NullReference only at CurrentStatus (Engineer, User tables succesfully fetching with Including and etc).

  public static async Task<ObservableCollection<Repair>> GetFullRepairs()
    {
         using (var cntx = new ServiceDBContext())
            { 

            ObservableCollection<Repair> tmp = new ObservableCollection<Repair>();
            await (cntx.Repairs.Include(xx => xx.Engineer).Include(zz=> zz.CurrentStatus).Include(yy => yy.User).ForEachAsync(new Action<object>((object obj) => { tmp.Add((Repair)obj); })));
            return tmp;
        }
    }

At this time LazyLoading disabled but no changes.

database was dropped 5 times, recompiled, google doesn't work. :( Maybe someone can help me! Thanks!

Upvotes: 1

Views: 95

Answers (2)

user1576474
user1576474

Reputation: 674

Firstly, this code is BAD. Do not use static methods - use repository pattern. Answer! This code have mistake at GetFullRepairs(). Need to working:

(((((cntx.Repairs.Include(ee=> ee.User)).Include(xx => xx.Engineer)).Include(yy => yy.RepairStatus))

Upvotes: 0

Benjamin Abt
Benjamin Abt

Reputation: 1838

It is not very intelligent to create a context in every method. Take a look at the repository pattern and inject the context via dependency injection. You code is nearly not testable. Personally I get eye cancer if I see static methods for this functionallity :-)

It could be an afteraffect because of that non-shared contexts. Which EF Version is here used? 6? 5? Any reason to not use FluentAPI for your schema? The fluent notation gives more error functionality and information if something is mapped wrong.

Upvotes: 1

Related Questions