I'm a beginner with EF and although I have managed to use it successfully before, I can't understand a simple problem I'm facing right now.
I have the following tables in my project.
MessageUser Table
public MessageUser()
{
this.Conversation = new HashSet<Conversation>();
}
public int MessageUserID { get; set; }
public string ConnectionID { get; set; }
public string UserName { get; set; }
public ICollection<Conversation> Conversation { get; set; }
ChatMessage Table
public class ChatMessage
{
public int ChatMessageID { get; set; }
public string Username { get; set; }
public string Message { get; set; }
public bool DeliveryStatus { get; set; }
public DateTime CreatedAt { get; set; }
public int ConversationID { get; set; }
[ForeignKey("ConversationID")]
public Conversation Conversation { get; set; }
}
Conversation Table
public class Conversation
{
public Conversation()
{
ChatMessages = new List<ChatMessage>();
MessageUsers = new List<MessageUser>();
}
public int ConversationID { get; set; }
public DateTime CreatedAt { get; set; }
public ICollection<MessageUser> MessageUsers { get; set; }
public ICollection<ChatMessage> ChatMessages { get; set; }
}
The 'ChatMessage' table and 'Conversation' table have a one-many relationship whereas 'Conversation' and 'MessageUser' have many-many relationship.
I'm trying to save the data as follows:
DataModel db = new DataModel();
List<MessageUser> ConnectedUsers = db.MessageUsers.ToList();
ChatMessage message = new ChatMessage { CreatedAt = DateTime.Now, DeliveryStatus = true, Message = "Hello My name is Mujtaba", Username = "Mujtaba" };
Conversation conversation = new Conversation();
conversation.ChatMessages.Add(message);
conversation.CreatedAt = DateTime.Now;
foreach (var user in ConnectedUsers)
{
conversation.MessageUsers.Add(user);
}
What I'm trying to understand is, when I add a 'MessageUser' to the collection property of 'Conversation', why isn't an inverse relation is established? What I mean to say is, after I add the entity to the collection, that entity's collection property should also show the object in it's collection to which is was added.
In the image above, the collection property of the entity which was added to 'Conversation' table has 0 'Conversation' objects. Is it supposed to be this way or am I doing something wrong?
Entity framework is lazy by default,how about trying to include the Conversation when you fetch the MessageUsers. i.e something like this
List<MessageUser> ConnectedUsers = db.MessageUsers.Include(x=>x.Conversation)ToList()
As you pointed out in the comment,the association/mapping is all good and that's the way EF works by default(being lazy).
Related
I will try to keep this one short. Any help is welcome and appreciated!
I have 2 classes that have a many-to-many relationship and their composite key class. When I'm creating a new "appointment" I want to pick "dental procedures" that are in the system.
All works fine until I reach the AppointmentRepository where I try to save the newly created appointment. The error is as stated.
I tried to add the
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
or
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
annotations above the DentalProcedureId property in the DentalProcedure class, but nothing works. Saved the changes and dropped tables, deleted all of the migrations etc.
DentalProcedure class:
public class DentalProcedure
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DentalProcedureId { get; set; }
[Required(ErrorMessage = "The name of the procedure must be specified")]
public string ProcedureName { get; set; }
[Required(ErrorMessage = "The price of the procedure must be specified")]
public decimal ProcedurePrice { get; set; }
public bool isEnabled { get; set; }
public List<CustomerProcedure> CustomerProcedures { get; set; }
public List<AppointmentProcedure> AppointmentProcedures { get; set; }
}
Appointment class:
public class Appointment
{
[Key]
public int AppointmentId { get; set; }
[Required]
public DateTime AppointmentStart { get; set; }
[Required]
public DateTime AppointmentEnd { get; set; }
[Required]
public string Title { get; set; }
public string ProcedureDescription { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public int WorkDaysId { get; set; }
public WorkDays WorkDays { get; set; }
public List<AppointmentProcedure> AppointmentProcedures { get; set; }
}
AppointmentProcedure class:
public class AppointmentProcedure
{
public int AppointmentId { get; set; }
public Appointment Appointment { get; set; }
public int DentalProcedureId { get; set; }
public DentalProcedure DentalProcedure { get; set; }
public bool ProcedureAppointmentCanceled { get; set; }
}
Home controller:
Appointment appointment = new Appointment
{
AppointmentStart = model.AppointmentStart,
AppointmentEnd = model.AppointmentEnd,
Title = model.Title,
ProcedureDescription = model.ProcedureDescription,
CustomerId = Id,
WorkDaysId = workkWeek.WorkDaysId,
};
foreach (var proc in model.DentalProcedures)
{
if (proc.isEnabled)
{
appointment.AppointmentProcedures = new List<AppointmentProcedure>
{
new AppointmentProcedure
{
Appointment = appointment,
DentalProcedure = proc,
ProcedureAppointmentCanceled = false
}
};
}
}
_appointment.CreateAppointment(appointment);
And the error :
Thanks once more in advance.
PS: I'm still learning so if I forgot to mention something, I apologize in advance!
Entity Framework needs to track the object in the database so you need to either attach or (what I usually prefer to do) load it from the database. Here's the changed code:
foreach (var proc in model.DentalProcedures)
{
if (proc.isEnabled)
{
//assuming the DBSet is called Procedures
var dbProc = await db.Procedures.FirstOrDefaultAsync(p => p.DentalProcedureId == id));
appointment.AppointmentProcedures = new List<AppointmentProcedure>
{
new AppointmentProcedure
{
Appointment = appointment,
DentalProcedure = dbProc, //now set the loaded entity
ProcedureAppointmentCanceled = false
}
};
}
}
using ServiceStack;
using ServiceStack.OrmLite;
public static string SqliteFileDb = "~/App_Data/db.sqlite".MapHostAbsolutePath();
private static void CreateX(Message msg)
{
//Using Sqlite DB- improved
var dbFactory = new OrmLiteConnectionFactory(SqliteFileDb, SqliteDialect.Provider);
// Wrap all code in using statement to not forget about using db.Close()
using (var db = dbFactory.Open())
{
db.CreateTableIfNotExists<Message>();
Message notex = new Message();
notex.Content = msg.Content;
notex.Datestamp = msg.Datestamp;
notex.Facility = msg.Facility;
notex.Hostname = msg.Hostname;
notex.LocalDate = msg.LocalDate;
notex.RemoteIP = msg.RemoteIP;
notex.Severity = msg.Severity;
db.Save(notex))
db.Close();
}
}
public class Message
{
public FacilityType Facility { get; set; }
public SeverityType Severity { get; set; }
public DateTime Datestamp { get; set; }
public string Hostname { get; set; }
public string Content { get; set; }
public string RemoteIP{ get; set; }
public DateTime LocalDate { get; set; }
}
Can someone advice how to address this
case where I am saving a syslog message
to an sqlite db using servicestack orm.
Seems only one object is always available
and gets updated.Hence no new record getting
created.
If you don't provide a Primary Key in OrmLite, OrmLite will assume the primary key is the first property in the table which is not what you want in this case. You either need to tell OrmLite which property it should use for the Primary Key by annotating it with the [PrimaryKey] attribute, or just add an auto incrementing primary key which the database will populate itself, e.g:
public class Message
{
[AutoIncrement]
public in Id { get; set; }
public FacilityType Facility { get; set; }
public SeverityType Severity { get; set; }
public DateTime Datestamp { get; set; }
public string Hostname { get; set; }
public string Content { get; set; }
public string RemoteIP{ get; set; }
public DateTime LocalDate { get; set; }
}
Also db.Close() is redundant in a using statement and there's no feature you want to use with OrmLite's high-level Save() API in this case so you should just have:
using (var db = dbFactory.Open())
{
db.CreateTableIfNotExists<Message>();
Message notex = new Message();
notex.Content = msg.Content;
notex.Datestamp = msg.Datestamp;
notex.Facility = msg.Facility;
notex.Hostname = msg.Hostname;
notex.LocalDate = msg.LocalDate;
notex.RemoteIP = msg.RemoteIP;
notex.Severity = msg.Severity;
db.Insert(notex);
}
I have this table
public class Unity
{
public int Id {get;set }
public string Name{ get; set; }
}
public class UsersRight
{
public int Id {get;set }
public string Name{ get; set; }
public int Value{ get; set; }
}
I need a list of all unities that user have access.
I know to do this way:
var userRight = _DAL.UserRights(user).ToList();
var listUser = new List<Unity>;
foreach (var item in userRight)
{
listUser.add( new Unity(Name = item.Name, Id = item.Value));
}
How can I do this in a more efficient way?
In your scenario, the User entity should have a list of Unities:
public virtual ICollection<Unity> Unities { get; set; }
and the Unity entity should have a User:
public virtual User User { get; set; }
You can check this entity framework tutorial on how to configure one-to-many relationship.
I have this model (Animal Model):
public int Id { get; set; }
public int AnimalSpecieId { get; set; }
public int AnimalBreedId { get; set; }
public Nullable<int> ProtectorId { get; set; }
public Nullable<int> OwnerId { get; set; }
public string Name { get; set; }
public virtual Owner Owner { get; set; }
public virtual Protector Protector { get; set; }
Protector Model:
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string CellPhone { get; set; }
public string Email { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
Owner Model:
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string CellPhone { get; set; }
public string Email { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
When I insert this model at the first time, if
ProtectorID = 1
and
OwnerID = null
it's ok, but, and I try to update this model, changing to:
OwnerID = 1
and
ProtectorID = null
I get the error in title, someone can help me with that ?
I don't agree with the above answer. I am not sure whether it solved your problem permanently because the issue is not related with the null value assignment. The actual reason is related with DBContext. When we go for any SaveChanges the context needs to be dispatched properly in order to proceed with the next SaveChanges to insert another record into DB on the same item with a different foreign key. You just need to add the below line after your "context.SaveChanges()"
context.Entry(your object).State = System.Data.Entity.EntityState.Detached;
This will solve the conflicts. Multiple insertion with same context results in conflicts.
Apologize if my comments criticised your answer in any manner.
I found the problem, after read this msdn post, I was thinking and found out what was happening, in my repository when I will update my entity, I was forgeting to set null all the related entities.
Old code:
var oldAnimal = context.Animals.Find(animal.Id);
if (oldAnimal != null)
{
oldAnimal.AnimalBreed = context.AnimalBreeds.Find(animal.AnimalBreed.Id);
oldAnimal.AnimalSpecie = context.AnimalSpecies.Find(animal.AnimalSpecie.Id);
oldAnimal.OwnerId = animal.OwnerId;
oldAnimal.ProtectorId = animal.ProtectorId;
oldAnimal.Castrated = animal.Castrated;
oldAnimal.DateBirth = animal.DateBirth;
oldAnimal.Gender = animal.Gender;
oldAnimal.Name = animal.Name;
oldAnimal.UpdateDate = DateTime.Now;
oldAnimal.Vaccinated = animal.Vaccinated;
oldAnimal.Weight = animal.Weight;
}
context.SaveChanges();
return animal;
new code:
var oldAnimal = context.Animals.Find(animal.Id);
if (oldAnimal != null)
{
oldAnimal.AnimalBreed = context.AnimalBreeds.Find(animal.AnimalBreed.Id);
oldAnimal.AnimalSpecie = context.AnimalSpecies.Find(animal.AnimalSpecie.Id);
oldAnimal.Owner = null;
oldAnimal.Protector = null;
oldAnimal.OwnerId = animal.OwnerId;
oldAnimal.ProtectorId = animal.ProtectorId;
oldAnimal.Castrated = animal.Castrated;
oldAnimal.DateBirth = animal.DateBirth;
oldAnimal.Gender = animal.Gender;
oldAnimal.Name = animal.Name;
oldAnimal.UpdateDate = DateTime.Now;
oldAnimal.Vaccinated = animal.Vaccinated;
oldAnimal.Weight = animal.Weight;
}
context.SaveChanges();
return animal;
virI'm trying to create a lookup table in my ASP.NET MVC4 application with Entity Framework Code First. It is for our locations and there should be two entries. I need to have some sort of ID associated with them so there is a LocationID stored in my Software table. However, when I create them there is an entry to created for each row in the Software Table.
Here is my Software class:
public class Software
{
public int Id { get; set; }
public virtual List<SoftwareType> SoftwareTypes { get; set; }
public virtual List<Location> Locations { get; set; }
public virtual List<SoftwarePublisher> Publishers { get; set; }
[Required]
[StringLength(128)]
public string Title { get; set; }
[Required]
[StringLength(10)]
public string Version { get; set; }
[Required]
[StringLength(128)]
public string SerialNumber { get; set; }
[Required]
[StringLength(3)]
public string Platform { get; set; }
[StringLength(1000)]
public string Notes { get; set; }
[Required]
[StringLength(15)]
public string PurchaseDate { get; set; }
public bool Suite { get; set; }
public string SubscriptionEndDate { get; set; }
//[Required]
//[StringLength(3)]
public int SeatCount { get; set; }
}
Here is my Locations class:
public class Location
{
public int Id { get; set; }
[Required]
[StringLength(20)]
public string LocationName { get; set; }
public virtual Software Software { get; set; }
}
Here is my Global.asax call to a seed method:
Database.SetInitializer(new SampleData());
using (var context = new Context())
{
context.Database.Initialize(true);
}
Here is my context:
public class Context : DbContext
{
public DbSet<Software> Software { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<SoftwarePublisher> SoftwarePublishers { get; set; }
public DbSet<SoftwareType> SoftwareTypes { get; set; }
public Context()
{
Configuration.ProxyCreationEnabled = false;
}
}
And here is my seeding:
public class SampleData : CreateDatabaseIfNotExists<Context>
{
protected override void Seed(Context context)
{
new List<Software> {
new Software {
Title = "Adobe Creative Suite",
Version = "CS6",
SerialNumber = "1234634543",
Platform = "Mac",
Notes = "Macs rock!",
PurchaseDate = "2012-12-04",
Suite = true,
SubscriptionEndDate = null,
SeatCount = 0,
SoftwareTypes = new List<SoftwareType>
{ new SoftwareType { Type="Suite" }},
Locations = new List<Location>
{ new Location { LocationName = "Paradise" }},
Publishers = new List<SoftwarePublisher>
{ new SoftwarePublisher { Publisher = "Adobe" }}},
...other similar rows...
}.ForEach(s => context.Software.Add(s));
base.Seed(context);
context.SaveChanges();
}
Because I am creating a new list for things like Locations (I need to fix the other things like SoftwareType and Publisher, but let's focus on Locations), it is creating a new row in my Locations table. How do I restructure my classes, so that I have two entries in my Locations table and then IDs in my Software table pointing to one of those two entries? Please bear in mind I am an Entity Framework newbie, so please try to be explicit. Thanks.
I think you want a many to many relationship between Software and Locations. To do that, you'll need to create a join table (also called a linking table). I believe you want to do this in your OnModelCreating override
this.HasMany(i => i.Softwares)
.WithMany(c => c.Locations)
.Map(mc =>
{
mc.MapLeftKey("SoftwareId");
mc.MapRightKey("LocationId");
mc.ToTable("SoftwareLocations");
});
I got that snippet from this blog post