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.
Related
I have a problem, in database I have an object of class Catalog with 3 elements in CatalogUsers. I want to replace that list with 2 element list being the orginal one without 1 position. My aim is to delete element that miss in new list.
CatalogUsers is many to many relation beetwen Catalog and User
public class Catalog
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string DefaultCurrencyCode { get; set; }
public virtual Currency? DefaultCurrency { get; set; }
public virtual List<CatalogUser>? CatalogUsers { get; set; }
}
public class CatalogUser
{
public int CatalogId { get; set; }
public virtual Catalog? Catalog { get; set; }
public int UserId { get; set; }
public virtual User? User { get; set; }
public CatalogUserRole Role { get; set; }
}
public class User
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string? Username { get; set; }
public string? Email { get; set; }
public string? Password { get; set; }
public virtual List<CatalogUser>? CatalogUsers { get; set; }
}
For now i tried to do it this way:
public async Task<int> UpdateAsync(Catalog model)
{
var _catalog = await GetByIdAsync(model.Id);
if (_catalog is null) return 0;
_catalog.Name = model.Name;
_catalog.DefaultCurrencyCode = model.DefaultCurrencyCode;
_catalog.CatalogUsers = model.CatalogUsers;
_context.Catalogs.Update(_catalog);
return await _context.SaveChangesAsync();
}
But it does not work the missing element is still in DB
This can be done using Cascaded Delete, as described in the docs.
But for cascading to happen, the tracked entities need to be modified. If you replace the list with a new list excluding the removed items, the tracking is severed. EF will try to match elements in the list to entities (or even add duplicates of entities in the database), but removed items will not be marked for deletion.
You need to manually remove the items from the tracked entity. E.g.
public async Task<int> UpdateAsync(Catalog model)
{
var _catalog = await GetByIdAsync(model.Id);
if (_catalog is null) return 0;
_catalog.Name = model.Name;
foreach(var catalogUser in _catalog.CatalogUsers.
.Where(cuIdDb => !model.CatalogUsers
.Any(cuInModel =>
// comparing by value, not reference e.g.:
cuInModel.Id == cuIdDb.Id))
.ToList()) // required as you are modifying the list
{
_catalog.CatalogUsers.Remove(catalogUser);
}
//_context.Catalogs.Update(_catalog);// shouldn't be required due to entity tracking
return await _context.SaveChangesAsync();
}
I have a Menu that populates bases on Username or Roles. I would like to generate a list table of the SQL entries. Standard foreach, using a ViewModel that i generated. I think this can be simplified and maybe some items added to the 'ViewModel' to achieve this but i am not getting this correct.
Below is the View model, the db.MenuPermissions has ForeinKeys:
RoleId - AspNetRoles(Id)
UserId - AspnetUsers(Id)
public class MenuPermissionView
{
[Key]
public Guid Id { get; set; }
public string MenuId { get; set; }
public virtual Menu Menu_MenuId { get; set; }
[Required]
[DisplayName("Role")]
public string RoleId { get; set; }
[DisplayName("User")]
public string UserId { get; set; }
[DisplayName("Sort Order")]
public int? SortOrder { get; set; }
[Required]
[DisplayName("Is For Nav Bar")]
public bool IsForNavBar { get; set; }
[Required]
[DisplayName("Is Add")]
public bool IsAdd { get; set; }
[Required]
[DisplayName("Is Create")]
public bool IsCreate { get; set; }
[Required]
[DisplayName("Is Read")]
public bool IsRead { get; set; }
[Required]
[DisplayName("Is Update")]
public bool IsUpdate { get; set; }
[Required]
[DisplayName("Is Delete")]
public bool IsDelete { get; set; }
}
Here is the controller (Not Complete) I feel this could be cleaner and it clearly does not work the way it is.
I have tried to add Identity for roles and users but that did not seem to help. I would also like for it to just return a list and not a staticPagedList with values, I will be using dataTables that will take care of that.
public ActionResult Index()
{
List<MenuPermissionView> col_Mem = new List<MenuPermissionView>();
var role = RoleManager.Roles
.ToList();
var user = UserManager.Users
.ToList();
var result =
foreach (var item in result)
{
MenuPermissionView objRoles = new MenuPermissionView();
objRoles.Menu_MenuId = item.Menu_menuId;
objRoles.RoleId = item.Name;
objRoles.UserId = item.Users;
objRoles.IsForNavBar = item.IsForNavbar;
objRoles.IsAdd = item.IsAdd;
objRoles.IsCreate = item.IsCreate;
objRoles.IsDelete = item.IsDelete;
objRoles.IsRead = item.IsRead;
objRoles.IsUpdate = item.IsUpdate;
col_Mem.Add(objRoles);
}
return View(col_Mem.ToPagedList(5, 25));
//return View(db.MenuPermissions.ToList());
}
I could achieve this if it were only using tables that i have models for, however these tables to not have models because they are part of Entity Frameworks Identities.
Thanks for your help!
i think the problem is that you are foreaching on item that you are just creating
you may get the reult from the database and foreach on the result
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).
I have the following scenario. We need to be able to fill forms for some tables, examples Companies (Empresa in Spanish), however we want the administrator to be able to extend the entity itself with additional fields.
I designed the following classes, and I need to seed at least one row, however its unclear to me how to seed one row of type CampoAdicional
Entity class:
public abstract class Entidad
{
[Key]
public int Id { get; set; }
}
Company Class (Empresas)
public class Empresa : Entidad
{
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public virtual ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
And the Additional Fields (Campo Adicional)
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
public virtual Entidad Entidad { get; set; }
}
However I dont know how to seed this class or table, because entity should be of subtype Company
Obviously the typeof doesnt compile
context.CampoAdicionals.Add(new CampoAdicional() { Entidad = typeof(Empresa), Id = 1, NombreCampo = "TwitterHandle", TipoCampo = Tiposcampo.TextoUnaLinea });
Update 1: Please note that the additional fields are for the entire entity company not for each company.
Unfortunately, I don't think you'll be able to use EF to automatically create that kind of relationship. You might be able to do something similar with special getters and such:
public class Entidad
{
// stuff...
public IEnumerable<CampoAdicional> CamposAdicionales
{
get { return CampoAdicional.GetAll(this); }
}
}
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
protected string EntidadType { get; set; }
// You will need some mapping between Type and the EntidadType string
// that will be stored in the database.
// Maybe Type.FullName and Type.GetType(string)?
protected Type MapEntidadTypeToType();
protected string MapTypeToEntidadType(Type t);
[NotMapped]
public Type
{
get { return MapEntidadTypeToType(); }
// maybe also check that Entidad.IsAssignableFrom(value) == true
set { EntidadType = MapTypeToEntidadType(value); }
}
public static IEnumerable<CampoAdicional> GetAll(Entidad ent)
{
return context.CampoAdicionals
.Where(a => a.EntidadType == MapTypeToEntidadType(ent.GetType()));
}
}
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