Question

I am creating one to one relation ship using entity framework. I created two classes Student and IssueMaster. Both classes derived from base Entity class

public class Entity
{
    public Entity()
    {
        CreatedOn = DateTime.Now;
    }

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    public DateTime CreatedOn { get; set; }
}

public class Student : Entity
{
    public string Name { get; set; }
    public string MobileNo { get; set; }
    public string Address { get; set; }
}

public class IssueMaster : Entity
{
    public IssueMaster()
    {
        IssueDate = DateTime.Now;
    }

    public virtual Student Student { get; set; }
    public DateTime IssueDate { get; set; }
    public DateTime? ReturnDate { get; set; }
}

and mapping for Student and IssueMaster classes is,

public class StudentMap : EntityTypeConfiguration<Student>
{
    public StudentMap()
    {
        ToTable("Student");

        Property(x => x.Name)
            .IsRequired()
            .HasMaxLength(20);

        Property(x => x.MobileNo)
            .IsRequired()
            .HasMaxLength(10)
            .IsFixedLength();

       Property(x=>x.Address).HasMaxlength(50);
    }
}

public class IssueMasterMap : EntityTypeConfiguration<IssueMaster>
{
    public IssueMasterMap()
    {
        ToTable("IssueMaster");

        Property(x => x.IssueDate).IsRequired();
        Property(x => x.ReturnDate).IsOptional();
        HasRequired(x => x.Student);
    }
}

The save method for saving entity is

    public static bool Save<T>(T entity) where T : Entity
    {
        var context = SessionManager.GetCurrentContext;
        context.Entry(entity).State = EntityState.Added;
        return context.SaveChanges() > 0;
    }

the problem is when we save IssueMaster object in database, the Student object attached to that IssueMaster object also getting saved in database.

What to do in code so that, when we save IssueMaster object and Student object from database attached to that IssueMaster object, only save IssueMaster object? (like SaveOrUpdate method does in Nhibernate)

Was it helpful?

Solution

Your problem is setting entry state to Added - that changes whole object graph to Added state (i.e. including all referenced entities). You can avoid adding new Student entry if you will use StudentId property instead of using Student object in saved entity. Or if you will manually set entity state of student to Unchanged or Modified.

First option is making your Save method non-generic. That will allow you to set navigation property state directly:

 context.Entry(issueMaster.Student).State = EntityState.Unchanged;

But if you want to have generic method, your only option is reflection (which is performance hit). Thus all your entities are inherited from base Entity type, you can get properties which are inherited from Entity type and check their id. If it has Guid.Empty value, then object is new and was not saved in database, otherwise you should set appropriate state of entity:

public static bool SaveOnlyAddedEntities<T>(T entity) where T : Entity
{
    var context = new ZorgContext();
    context.Entry(entity).State = EntityState.Added;
    var entry = context.Entry(entity);

    Type type = typeof(T);
    var flags = BindingFlags.Instance | BindingFlags.Public;

    var unchangedDependencies =
        type.GetProperties(flags)
            .Where(p => p.PropertyType.IsSubclassOf(typeof(Entity)))
            .Select(p => (Entity)p.GetValue(entity))
            .Where(e => e.Id != Guid.Empty);

    foreach (var dependency in unchangedDependencies)
        context.Entry(dependency).State = EntityState.Unchanged;

    return context.SaveChanges() > 0;
}

Note: this approach shows working with non-collection properties.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top