How to rollback a removed entity to its previous state? - c#-4.0

Consider the following situation:
public class EntityA
{
public int Id { get; set; }
}
public class EntityB
{
public int Id { get; set; }
public virtual EntityA EntityA { get; set; }
}
public class Context : DbContext
{
public Context()
: base("EF_SPIKE")
{
}
public IDbSet<EntityA> EntityAs { get; set; }
public IDbSet<EntityB> EntityBs { get; set; }
}
static void Main(string[] args)
{
using (var context = new Context())
{
var a = context.EntityAs.Create();
context.EntityAs.Add(a);
var b = context.EntityBs.Create();
b.EntityA = a;
context.EntityBs.Add(b);
context.SaveChanges();
using (var transaction = new TransactionScope())
{
context.EntityBs.Remove(b);
context.SaveChanges();
}
Trace.Assert(b.EntityA == null);
Trace.Assert(context.EntityBs.Local.All(x => x.Id != b.Id));
Trace.Assert(context.EntityBs.Any(x => x.Id == b.Id));
}
}
So although the transaction is rolled back and the entity still exists in the database, the entity framework entity b no longer exists in the local cache, loses all of its foreign key references and b can no longer be worked with.
This behaviour seems odd to me since although I've rolled back, my entity is still dead.
Is there any standard workaround to this so that:
Trace.Assert(context.EntityBs.Local.Any(x => x.Id == b.Id));
passes?

Related

EF Core Collections using Automapper.Collection.EntityFrameworkCore

Given I have 2 classes, Foo and Bar:
public class Foo
{
private readonly List<Bar> _bars = new List<Bar>();
public int Id { get; private set; }
public string Name { get; private set; }
public IEnumerable<Bar> Bars => _bars;
public void AddBar(Bar bar)
{
_bars.Add(bar);
}
public static Foo Create(string name)
{
return new Foo { Name = name };
}
private Foo() { }
}
public class Bar
{
public int Id { get; private set; }
public string Description { get; private set; }
public static Bar Create(string description)
{
return new Bar { Description = description };
}
}
With 2 corresponding DTOs,
public class BarDto
{
public int Id { get; set; }
public string Description { get; set; }
}
public class FooDto
{
public int Id { get; set; }
public string Name { get; set; }
public List<BarDto> Bars { get; set; }
public FooDto()
{
Bars = new List<BarDto>();
}
}
And an AutoMapper/AutoMapper.Collection.EntityFrameworkCore setup of
var config = new MapperConfiguration(cfg =>
{
cfg.AddCollectionMappers();
cfg.UseEntityFrameworkCoreModel<DemoContext>();
cfg.CreateMap<BarDto, Bar>().EqualityComparison((src, dest) => src.Id == dest.Id);
cfg.CreateMap<FooDto, Foo>().ForMember(dest => dest.Bars, opt =>
{
opt.MapFrom(s => s.Bars);
opt.UseDestinationValue();
}).EqualityComparison((src, dest) => src.Id == dest.Id);
});
I have a use case whereby the incoming FooDto may contain inserted, appended, updated and deleted items in the Bars collection which I am attempting to handle by:
Looking up the existing entity from the database
Mapping changes from the DTO to the entity
Saving the changes to the database
However the following code produces an InvalidOperationException exception stating that "The instance of entity type 'Bar' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached"
var fooToUpdate = db.Foos.Include(_ => _.Bars).FirstOrDefault(_ => _.Id == fooDto.Id);
mapper.Map(fooDto, fooToUpdate);
db.SaveChanges();
My understanding was that becuase I am setting EqualityComparison for the BarDto -> Bar mapping it should update the tracked entity and the save operation should succeed becuase it was referencing the same object?
I am not sure if I'm going about this the wrong way or simply missing somthing in the configuration.
Update
It seems the problem I am facing may be related to this issue on github.

AutoMapper .ReverseMap() .Ignore() not working

Having an issue with version 6.1.1. In the below, the result of the reverse map still has the Company object populated. Per this post, which shows what I am doing below, except they are ignoring a property, and I'm ignoring a complex object.
What am I missing?
CreateMap<Item, ItemViewModel>(MemberList.Destination)
.ReverseMap()
.ForMember(x => x.Company, x => x.Ignore())
;
With AutoMapper 6.1 you could use ForPath instead ForMember to ignore complex objects.
See How to ignore property with ReverseMap for further information.
I see not what is wrong, but here is a running sample:
namespace AutomapperTest2
{
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantEducation, ApplicantEducationVM>();
cfg.CreateMap<Applicant, ApplicantVM>().ReverseMap()
.ForMember(x => x.Education, x => x.Ignore());
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
Applicant ap = new Applicant
{
Name = "its me",
Education =
new ApplicantEducation
{
SomeInt = 10,
SomeString = "sampleString"
}
};
// Map
ApplicantVM apVm = Mapper.Map<Applicant, ApplicantVM>(ap);
Applicant apBack = Mapper.Map<ApplicantVM, Applicant>(apVm);
}
#endregion
}
/// Your source classes
public class Applicant
{
public ApplicantEducation Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducation
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
// Your VM classes
public class ApplicantVM
{
public string Description { get; set; }
public ApplicantEducationVM Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducationVM
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
}
}

How to save ICollection data to database with MVC 5

I have searched and tried dozen of solutions for my problem, but cant get this working.
I have compressed the class definitions here, but here are the main parts.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Device = new HashSet<Device>();
}
public virtual ICollection<Device> Devices { get; set; }
}
public class Device
{
public Device()
{
}
public int Id { get; set; }
public string DeviceId { get; set; }
public virtual ApplicationUser User { get; set; }
}
UserController.cs:
public async Task<ActionResult> SaveDevice(string id, string selection)
{
var selectedDevice = db.Devices.Where(u => u.DeviceId.Equals(selection));
var user = await UserManager.FindByIdAsync(id);
user.Devices.Add(selectedDevice.First());
==> here everything looks ok, device data can be seen from the user data
==> in comments different things that I have tried so far, without getting anything to work
//db.Entry(user).Property(p => p.Devices).IsModified = true;
//db.Entry(user).Reference(p => p.Devices).IsLoaded = true;
//db.Entry(user).Collection(p => p.Devices).IsLoaded = true;
//db.Entry(user).State = EntityState.Modified;
db.Users.Attach(user);
db.SaveChanges();
return RedirectToAction("Index");
}
==> now, when I return to user details view, the device data is empty, so nothing has be stored to db

Automapper, Mapping one object member type to multiple concrete type

I have this Party class which contains an object data type coming from a service. It can contain two different member types for the Item property.
public class Party
{
public string DMVID {get; set;}
public object Item { get; set; }
}
and this DTO
public class PartyDTO
{
public string DMVID {get; set;}
public BusinessDTO BusinessItem { get; set; }
public IndividualDTO IndividualItem { get; set; }
}
How can I map the output of the Item to BusinessItem or IndividualItem.
I know this one would not work. Mapper.CreateMap<Party, PartyDTO>();
I don't know if conditional mapping can solve this or a resolver like this one.
Hey maybe this will help you out! I tested it, but i am using AutoMapper just for two days!
Allright here are your noted classes!!!
public class Party
{
public string DMVID { get; set; }
public object Item { get; set; }
}
public class PartyDTO
{
public string DMVID { get; set; }
public BuisnessDTO BusinessItem { get; set; }
public IndividualDTO IndividualItem { get; set; }
}
public class BuisnessDTO
{
public int Number
{
get;
set;
}
}
public class IndividualDTO
{
public string Message
{
get;
set;
}
}
and here your is the MapperConfiguration for this current scenario!
// Edit There was no need here for some conditions
AutoMapper.Mapper.CreateMap<Party, PartyDTO>()
.ForMember(dto => dto.BusinessItem, map =>
map.MapFrom(party => party.Item as BuisnessDTO);
)
.ForMember(dto => dto.IndividualItem, map =>
map.MapFrom(party => party.Item as IndividualDTO);
);
// And this is another way to achive the mapping in this scenario
AutoMapper.Mapper.CreateMap<PartyDTO, Party>()
.ForMember(party => party.Item, map => map.MapFrom( dto => (dto.BusinessItem != null) ? (dto.BusinessItem as object) : (dto.IndividualItem as object)));
And i created this sample for it!
Party firstParty = new Party()
{
DMVID = "something",
Item = new BuisnessDTO()
{
Number = 1
}
};
Party secondParty = new Party()
{
DMVID = "something",
Item = new IndividualDTO()
{
Message = "message"
}
};
PartyDTO dtoWithBuisness = AutoMapper.Mapper.Map<PartyDTO>(firstParty);
PartyDTO dtoWithIndividual = AutoMapper.Mapper.Map < PartyDTO>(secondParty);
Party afterParty = AutoMapper.Mapper.Map<Party>(dtoWithBuisness);
afterParty = AutoMapper.Mapper.Map < Party>(dtoWithIndividual);
Of course there are other possibility, but I think thats exactly what you wanted.

Servicestack OrmLite deleting many to many

Let's say I have a ListingEvent class and a UserAccount class.
A ListingEvent can have many UsersAttending and a UserAccount can attend many ListingEvents.
The classes look like:
public class UserAccount
{
[AutoIncrement]
[PrimaryKey]
public int Id {
get ;
set;
}
public string Name {
get ;
set;
}
public UserAccount()
{
ListingEventsAttending = new List<UserAccountListingEvent> ();
}
[Reference]
public List<UserAccountListingEvent> ListingEventsAttending {
get;
set;
}
}
public class UserAccountListingEvent{
[AutoIncrement]
[PrimaryKey]
public int Id {
get;
set;
}
public Model.AttendingStatus Status { get; set; }
[References(typeof(UserAccount))]
public int UserAccountId {
get;
set;
}
[References(typeof(ListingEvent))]
public int ListingEventId {
get;
set;
}
}
public class ListingEvent
{
public ListingEvent()
{
UsersAttending = new List<UserAccountListingEvent>();
}
[AutoIncrement]
[PrimaryKey]
public int Id {
get ;
set;
}
public string Name { get; set; }
[Reference]
public List<UserAccountListingEvent> UsersAttending { get; set; }
public void RemoveUserAttending(UserAccount user)
{
if (user == null)
{
return;
}
UsersAttending.RemoveAll(u => u.UserAccountId == user.Id);
}
}
And I get a ListingEvent that has my UserAccount attending with:
var listingEvent = db.LoadSingleById<Model.ListingEvent> (request.Id);
And I can see that the user with the correct Id is attending so call RemoveUserAttending to remove the user. I can now see the user is not attending so I call:
db.Save (listingEvent, references: true);
But - now when I go to fetch that ListingEvent again the user is back to attending.
So my question is:
should the above work as expected?
if not - how should I be doing this?
db.Save() only INSERT or UPDATE entities i.e. it doesn't DELETE them.
To delete, retrieve the entities or entity Ids you want to delete and use OrmLite's db.Delete* API's explicitly, e.g. something like:
var removeUsersAttendingIds = listingEvent.UsersAttending
.Where(u => u.UserAccountId == user.Id)
.Select(u => u.Id);
db.DeleteByIds<UserAccountListingEvent>(removeUsersAttendingIds);

Resources