Follow the methods below:
public class User
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
}
public class DataContext : DbContext
{
DbSet<User> Users { get; set; }
}
public class Repository
{
DataContext db = new DataContext();
public User Attach1(User entity)
{
var ent = db.Entry<User>(entity);
ent.State = EntityState.Modified;
if (db.SaveChanges() > 0)
return ent.Entity;
return null;
}
public User Attach2(User entity)
{
return db.Users.Attach(entity);
}
}
Is there any difference between Attach1 and Attach2?
Your Attach1 and Attach2 methods perform different things and it is not clear what you expect to do in these methods. When you attach an entity to EF it will be added to the context in Unchanged state. If you modify the entity after attaching, then EF will track those changes and the entity will be in Modified state.
Attach1
This method will attach an entity and mark it as modified. So the subsequent SaveChanges() will update all the properties of the entity. Calling SaveChanges() inside the Attach method is not recommended since it does more than attaching.
Attach2
This method will attach the entity as Unchanged.
Related
So it happens that you can prevent breeze json serialization of some properties using data annotations on your model by like this(well if you are using EF6 with JSON.NET on the backend)...
[Table("Project")]
public partial class Project
{
public Project()
{
}
public int id { get; set; }
[JsonIgnore]
public bool NoLongerExist { get; set; }
}
By doing so the property becomes invisible on this endpoint used by breeze
public IQueryable<Project> Projects()
{
return _db.Context.Projects.Where(o => o.NoLongerExist == true);
}
Can i apply [JsonIgnore] based on a certain condition like an authenticated user or a random if from this endpoint?
First I'm using mvvmcross version 3.0.13. When a class is inherited from a base class, which is inherited from MvxViewModel, the Init method won't be called from IoC. Current ugly workaround is to call the Init in the constructor. Could this be a bug or is there another pattern to use?
In both classes (base and child of that base) it won't be called. For example:
public class BaseViewModel : MvxViewModel
{
protected CDataImportService DataImportService { get; private set; }
protected CSettingService SettingService { get; private set; }
protected CDataService DataService { get; private set; }
protected CDocumentService DocumentService { get; private set; }
public BaseViewModel(IDataService objDataService, IDataImportService objDataImportService, IDocumentService objDocumentService, ISettingService objSettingService)
{
DataImportService = (CDataImportService)objDataImportService;
SettingService = (CSettingService)objSettingService;
DataService = (CDataService)objDataService;
DocumentService = (CDocumentService) objDocumentService;
}
public void Init()
{
Mvx.Trace("Init called in {0}", GetType().Name);
}
}
public class DocumentsViewModel : BaseViewModel
{
public MenuViewModel(IDataService objDataService, IDataImportService objDataImportService, IDocumentService objDocumentService, ISettingService objSettingService)
: base(objDataService, objDataImportService, objDocumentService, objSettingService)
{
}
}
IOC on it's own does not call the Constructor-Init-Reload-Start sequence.
IOC is a general C# service, and only calls the constructor part.
If you want the entire sequence called then you can access this via the IMvxViewModelLoader object - e.g Mvx.Resolve<IMvxViewModelLoader>().LoadViewModel(MvxViewModelRequest<MyViewModel>.GetDefaultRequest(), null);
By default, this will use the Default ViewModel Locator to create a view model instance - https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross/ViewModels/MvxDefaultViewModelLocator.cs
If it helps, for some more information:
mvvmcross IOC is described fully in https://github.com/MvvmCross/MvvmCross/wiki/Service-Location-and-Inversion-of-Control
view model location is briefly discussed in https://github.com/MvvmCross/MvvmCross/wiki/Customising-using-App-and-Setup
As far as I did research on defining many to many relations with Code First, I guess that custom constructors in the entity classes are only needed for the purpose of being able to create a new instance of an entity plus the n:m-related entity AT ONCE.
At the moment I have my classes defined like this:
public class Person
{
public Person()
{
Events = new HashSet<Event>();
}
public int PersonId { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
public class Event
{
public Event()
{
Persons = new HashSet<Person>();
}
public int EventId { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
However, if my application will never offer the possibility to create a new Person during creating a new Event, can I simply omit the custom constructor for Events?
public class Event
{
public int EventId { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
Will the many to many relation still work fine?
If you do that you'll get a NullReferenceException when you create a new event and try to add Persons to it.
var #event = new Event();
event.Persons.Add(new Person()); //NullReferenceException here
that is the only reason for that constructor, to initialise the collections.
you can initialise the Persons collection lazily inside the getter on first access but you need to be careful with multithreading.
How can I create+persist+have-a-proxy-to a new instance of a code-first poco using only navigation property collections? In the bellow code, I show how you might want to do this, if using member functions to create POCOs that then create POCOs. You don't have a DbContext, but if you create an object and persist it using DbSet.Add, the returned object isn't a proxy, so you can't in turn use its DbSet.Add to add a different sub-object.
In this code, if you call MailingList.AddIncomingMessage("my message"), you get an exception at the "OOPS" comment, because the created msg isn't a proxy and thus its Message.doodads property is null.
class Doodad {
public int ID { get; set; }
public string doodad { get; set; };
}
class Message {
public int ID { get; set; }
public virtual MailingList mailingList { get; set; }
public virtual ICollection<Doodad> doodads { get; set; }
public string text { get; set; }
public void GetDoodadCreateIfNeeded(string doodad) {
try {
// won't be found since we just created this Message
return this.doodads.First(d => d.doodad == doodad);
} catch (Exception e) {
Doodad newDoodad = new Doodad() { doodad=doodad };
// OOPS! this.doodads == null, because its not a proxy object
this.doodads.Add(newDoodad);
return newDoodad;
}
}
}
class MailingList {
public int ID { get; set; }
public virtual ICollection<Message> messages { get; set; }
public void AddIncomingMessage(string message) {
var msg = new Message() { text=message };
// we have no Context, because we're in a POCO's member function
this.messages.Add(msg);
var doodad = msg.GetDoodadCreateIfNeeded("bongo drums");
}
}
EDIT: sorry guys, I forgot to put the property accessors and ID in for this simplified case, but I am using them in the actual code.
It has nothing to do with proxies. It is the same as any other code - if you want to use object / collection you must first initialize it! Your fist command:
return this.doodads.First(d => d.doodad == doodad);
doesn't throw exception because it didn't find doodad but because the doodads is null.
What do you need to do? You need to initialize collections before you first use them. You can do it:
Directly in their definition
In entity's constructor
In property getter (lazy initialization) once they are first needed - that would require to change your fields to properties which is btw. correct way to write classes in .NET
In your custom methods you can check if they are null and initialize them
Complementary to the navigation property, you need to have a property that is the Id of the foreign key.
So your MailingList will need to have this property:
[Key] // this attribute is important
public int Id { get; set; }
and you'll have to change the Message classe to have these properties:
public virtual int mailingListId { get; set;
public virtual MailingList mailingList { get; set; }
The { get; set; } property is important, so that it is a property, not just a public attribute.
I noticed something interesting when I was performing a delete using EF code first. I use the following domain model:
public class User
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Playlist> Playlists { get; set; }
}
public class Playlist
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Track> Tracks { get; set; }
}
public class Track
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
public virtual Playlist Playlist { get; set; }
}
The model is configured using:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Playlists).WithRequired(x => x.User).Map(x => x.MapKey("UserId"));
modelBuilder.Entity<Playlist>().HasMany(x => x.Tracks).WithRequired(x => x.Playlist).Map(x => x.MapKey("PlaylistId"));
}
I use a generic repository:
public virtual void Delete(T entity)
{
Database.Set<T>().Remove(entity);
}
I also have a dto that looks like:
public class PlaylistDTO
{
public PlaylistDTO(Playlist playlist)
{
Id = playlist.Id;
Title = playlist.Title;
User = playlist.User.Name;
}
}
In one of my services I am trying to do the following:
public PlaylistDTO Delete(long id)
{
Playlist playlist = playlistRepository.GetById(id);
playlistRepository.Delete(playlist);
unitOfWork.Commit();
return PlaylistDTO(playlist);
}
This code fails. When I stepped through the debugger I noticed something interesting. The moment I call playlistRepository.Delete the navigational properties (User and Tracks) get set to null and empty respectively. Playlist however stays in memory. So when I pass in the playlist to the DTO the code will fail when it is trying to access playlist.User.Name. I wanted to pass this data to the client to display a verification.
Is this behavior correct? Is this by design?
This is how EF works. The problem is that your Playlist forms entity graph with other relations and EF uses very simple rule for tracking entity graphs: All entities in the graph must be tracked - there cannot be reference to entity which is not tracked. I don't give you reference to description of this rule, it is just my observation but I didn't find any single exception to this rule.
Edit: Updated version - I just checked internal implementation and relations are indeed nulled during calling Delete
So what happened in your code.
You marked your Playlist as deleted
EF passes delete operation to the state manager which does the fixup - it will null all relations
You saved changes to the database
Because there are no cascade deletes from Playlist all related objects remain undeleted
Once you saved changes EF internally accepted them and set change tracker to current state
Because the current state of Playlist is non existing (deleted in the database) it was detached from the context
Detaching has broken entity graph and EF fixed it by modifying navigation properties on both ends
The code responsible for nulling from System.Data.Objects.EntityEntry.Delete(doFixup) (doFixup is true) - the class is internal:
if (doFixup && (base.State != EntityState.Deleted))
{
this.RelationshipManager.NullAllFKsInDependentsForWhichThisIsThePrincipal();
this.NullAllForeignKeys();
this.FixupRelationships();
}
In your scenario this should have simple workaround - create DTO before you delete entity.