Entity Framework 5 One to One relationship (e.g. User -> Profile) - ModelBuilder ASP.NET MVC4 - asp.net-mvc-4

I am trying to do a similar thing to what this previous answer had here:
How to declare one to one relationship using Entity Framework 4 Code First (POCO)
The problem is, im very new to this and am using Entity Framework 5 code first and the HasConstraint doesnt exist anymore, not to mention Im not good at lamda. I was wondering if anyone could help expand on this so I can map a User class to a Profile class effectively and easily? I need to know how to do this for the configuration files and model builder
Each user has one profile
Also, another quick question, say the profile model had Lists in this, how would I put these effectively in the model builder and configuration files?
Thank you

e.g.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public Profile Profile { get; set; }
// public int ProfileId { get; set; }
}
public class Profile
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PostalCode { get; set; }
}
// ...
modelBuilder.Entity<User>()
.HasOptional(x => x.Profile)
.WithRequired();
ProfileId is useless, FK is on the 'other side of the fence' (in Profile).
(this makes most sense IMO)
If you do need an Id in User (e.g. to be able to fill in Profile just by its ID when adding User - which if one-to-one is not really used - as you create both profile and user), then you can reverse...
modelBuilder.Entity<User>()
.HasRequired(x => x.Profile)
.WithOptional();
...and your ProfileId is actually in the Id (pk -> pk).

That solution worked for me
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasMaxLength(450);
entity.HasOne(d => d.Profile).WithOne(p => p.User);
});
modelBuilder.Entity<UserProfile>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasMaxLength(450);
entity.Property(e => e.Type)
.HasMaxLength(10)
.HasColumnType("nchar");
entity.HasOne(d => d.User).WithOne(p => p.Profile);
});
}

Related

Creating an index on shadow property

I am implementing a model containing a number of owned types and need to create indexes on properties of the owned types. As I understand I cannot create the indexes directly in Fluent API using builder.HasIndex(entity => entity.location.geo); but instead I should use a Shadow Property and create the index on this. I think I have implemented the Shadow Property (Geo) correctly, but the indexer doesn't work. Please help.
I'm using .NET5
Here is my model (simplified for readability)
public class Meeting
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public Location Location { get; set; }
}
public class Location
{
public Point Geo { get; set; }
}
And the configuration class:
public class MeetingConfiguration : IEntityTypeConfiguration<Meeting>
{
public void Configure(EntityTypeBuilder<Meeting> builder)
{
builder
.ToTable("Meetings")
.HasKey(c => c.Id);
builder
.Property(c => c.Id)
.HasColumnType("bigint")
.ValueGeneratedOnAdd()
.HasComment("Unique ID of the Meeting");
builder
.Property(c => c.Name)
.HasColumnType("varchar(127)")
.IsRequired()
.HasComment("Name of the Meeting");
builder
.Property(c => c.StartDate)
.HasColumnType("date")
.IsRequired()
.HasComment("First date of the Meeting");
// Configure Location owned entity
builder
.OwnsOne(p => p.Location, location =>
{
location
.Property(p => p.Name)
.HasColumnType("nvarchar(127)")
.IsRequired(false)
.HasComment("Optional: Name of the Location");
location
.Property(p => p.Geo)
.HasColumnType("geography (point)")
.IsRequired(false)
.HasComment("Optional: GPS Coordinates of the Location");
});
// Create Shadow properties for use in indexes
builder
.Property<Point>("Geo");
builder
.IndexerProperty<Point>("Geo");
// Create Indexes
builder
.HasIndex(entity => new
{
entity.Name,
entity.StartDate
});
}
}

Related table displaying nothing instead of name after attempting to look up name from related table

EDIT:
Changed a couple of classes compared to what they are below to match the Microsoft guide on Relationships.
Devices.cs
public partial class Devices
{
public int DeviceType { get; set; }
...
public DeviceTypes DeviceTypes { get; set; }
}
DeviceTypes.cs
public partial class DeviceTypes
{
public int DeviceType { get; set; }
public string Name { get; set; }
public Devices Devices { get; set; }
}
I then changed my view to start reading off of DeviceTypes.Name.
Index.cshtml
#Html.DisplayFor(modelItem => item.DeviceTypes.Name)
dbContext.cs
modelBuilder.Entity<Devices>(entity =>
{
entity.HasKey(e => e.DeviceId);
...
entity.Property(e => e.DeviceType).HasComment("ID of the type of devices. Primary key in DeviceTypes table.");
...
entity
.HasOne(p => p.DeviceTypes)
.WithOne(p => p.Devices);
});
....
modelBuilder.Entity<DeviceTypes>(entity =>
{
entity.HasKey(e => e.DeviceType);
entity.HasComment("The types of devices.");
entity.Property(e => e.DeviceType)
.HasComment("Id of the device.")
.ValueGeneratedNever();
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50)
.HasComment("Name of the device.");
entity
.HasOne(b => b.Devices)
.WithOne(i => i.DeviceTypes)
.HasForeignKey("DeviceType");
});
I also added to the model builder, but got stuck again not understanding. I've spent a few more hours trying but I can't figure it out. The current error I get is:
InvalidOperationException: You are configuring a relationship between 'DeviceTypes' and 'Devices' but have specified a foreign key on 'DeviceType'. The foreign key must be defined on a type that is part of the relationship.
This is a question about using related tables in my CRUD editors in .NET Core 3.1. I want to use the Name instead of the ID as a display on the index/details/delete pages. And on the create/edit pages I want to use a dropdown to select the value from a list.
There are quite a few examples I have found, but nothing I have tried yet has worked. I had too many different ideas mixed in my code before so I tossed all changes and I am trying again, but got stuck.
I am starting simple with adding my model again and trying to simply get it to display in my paginated list. Currently, the paged list displays no value at all rather than a name or ID. Obviously I am trying to get the Name to display. Then I will move toward something more complicated like getting this value in a dropdown on the create page.
I know I am not figuring out the part about linking the data from the database back into my view model, but I have spent so many hours on this today and haven't quite found the missing piece. I have had so many variations on this that didn't work and this was trying to start by keeping it as simple as I could and then ask you all for advice.
Here is the code I am working with:
Devices.cs - DeviceType is really the ID
public partial class Devices
{
[MaxLength(10)]
public string DeviceId { get; set; }
public int DeviceType { get; set; }
...
[NotMapped]
public DeviceTypes DeviceTypes { get; set; }
}
DeviceTypes.cs - This is what I want to look up by DeviceType (int) and use the name in the editors
public partial class DeviceTypes
{
public int DeviceType { get; set; }
public string Name { get; set; }
}
DevicesViewModel.cs
public class DevicesViewModel
{
public Devices Devices { get; set; }
public PaginatedList<Devices> DevicesPagedList { get; set; }
}
DevicesController.cs - This is in the Index method where I get the data and then later what I am passing into my view
var devices = from dev in _context.Devices
select dev;
...
return base.View(new DevicesViewModel { DevicesPagedList = await PaginatedList<Devices>.CreateAsync(devices.AsNoTracking(), currentPage, resultsPerPage ?? 10), ResultsPerPageList = SelectListItemHelper.ResultsPerPageList().Where(l => l.Text != (resultsPerPage ?? 10).ToString()) });
}
Finally, here is where I am trying to display the value in Index.cshtml
#model DevicesViewModel
...
#foreach (var item in Model.DevicesPagedList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DeviceTypes.Name)
</td>
...

Mapping Id to Model for controller API

I'm using asp.net core on a project. (I'm fairly new to it)
I have a User Model. the code below is a simplified version:
public class User
{
public int id { get; set; }
// attribute declaration
public ICollection<User> friends { get; set; }
}
I'm using automapper service to map my api to this Model:
public class UserResource
{
public UserResource()
{
this.friendsId = new List<int>();
}
public int id { get; set; }
// attribute declaration
public ICollection<int> friendsId { get; set; }
}
consider a post request to UserController with the following body:
{
"id" : 1
"friendsId": [2,3,4],
}
I want to map integers in friendsId to id of each user in friends collection. but I can't figure out what to do. here's what I've got:
CreateMap<UserResource,User>()
.ForMember(u => u.friends,opt => opt.MapFrom(????);
is this the right approach? if so how should I implement it?
or should I change my database model to this:
public class User
{
public int id { get; set; }
// attribute declaration
public ICollection<int> friendsId { get; set; }
}
Thank you in advance.
You'll need to implement a custom value resolver. These can be injected into, so you can access things like your context inside:
public class FriendsResolver : IValueResolver<UserResource, User, ICollection<User>>
{
private readonly ApplicationDbContext _context;
public FriendsResolver(ApplicationDbContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public ICollection<User> Resolve(UserResource source, User destination, ICollection<User> destMember, ResolutionContext context)
{
var existingFriendIds = destMember.Select(x => x.Id);
var newFriendIds = source.friendsId.Except(existingFriendIds);
var removedFriendIds = existingFriendIds.Except(source.Friends);
destMember.RemoveAll(x => removedFriendIds.Contains(x.Id);
destMember.AddRange(_context.Users.Where(x => newFriendIds.Contains(x.Id).ToList());
return destMember;
}
}
Not sure if that's going to actually work as-is, as I just threw it together here, but it should be enough to get your going. The general idea is that you inject whatever you need into the value resolver and then use that to create the actual stuff you need to return. In this case, that means querying your context for the User entities with those ids. Then, in your CreateMap:
.ForMember(dest => dest.friends, opts => opts.ResolveUsing<FriendsResolver>());
This only covers one side of the relationship, though, so if you need to map the other way, you may need a custom resolver for that path as well. Here, I don't think you actually do. You should be able to just get by with:
.ForMember(dest => dest.friendsId, opts => opts.MapFrom(src => src.friends.Select(x => x.Id));
This would help
CreateMap<UserResource,User>()
.ForMember(u => u.friends,opt => opt.MapFrom(t => new User {FriendsId = t.friendsId);
public class User
{
...
public ICollection<User> friends { get; set; }
}
Where friends is ICollection<User> whereas UserResource class has ICollection<int>. There is type mismatch here. You need to map ICollection to ICollection that is why I casted new User ...

Possible Bug in Migration for EF 6 Alpha 3 on recursive relationship?

I had to define a recursive relationship on a composite key. After much trials, I ended up with this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Category>()
.Property(t => t.WhichAmazon).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Category>()
.Property(t => t.IdCategory).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Category>()
.HasKey(c => new {c.WhichAmazon, c.IdCategory})
.HasOptional(p => p.Children)
.WithMany()
.HasForeignKey(c => new { c.WhichChildrenAmazon, c.ChildrenId });
}
for this table
public class Category
{
// Keys and relationships defined in BooksDataLayer
[MaxLength(2)]
public string WhichAmazon { get; set; }
public int IdCategory { get; set; }
public string Name { get; set; }
[Timestamp]
public Byte[] TimeStamp { get; set; }
public DateTime LastCheck { get; set; }
public virtual List<Book> Books { get; set; }
public string WhichChildrenAmazon { get; set; }
public int? ChildrenId { get; set; }
public virtual List<Category> Children { get; set; }
}
While trying to Add-Migration I was constantly having the same error: "Sequence contains no elements". As I was "almost" sure this definition was right, I went ahead a re-created a new Db, WITHOUT migration. Was perfectly OK, no problems with the Db at all. So there is "something" in there which EF 6 does not like. I had a confirmation, as EF power tools bombs if I try to get a schema "Exception has been thrown by the target of an invocation".
I'll see what happens now with migration if I restart from there, but I am afraid to not be able to use anymore with this Db. I do like the tool, a lot, so I hope this can be fixed.
The issue is an invalid configuration of the relationship.
The Fluent API call includes this:
.HasOptional(p => p.Children)
.WithMany()
This is invalid because Children is a collection navigation. The correct config is:
.HasMany(p => p.Children)
.WithOptional()
We are planning to take a fix to provide a better exception message post-EF6.
Opened a bug for this on the EF codeplex site: http://entityframework.codeplex.com/workitem/1015

How to Map a ValueObject Collection with Foreign Keys in FluentNHibernate

I've been looking all over for an example of this, but it seems pretty uncommon. Hopefully some NHibernate guru will know.
I have the following class which, by my understanding of Value Objects, is a Value Object. Assume every user has the ability to assign one or more tags to any Question (think Stack Overflow). The Tags don't need a primary key, but they do hold references to the User and Question, unlike most of the examples of ValueObjects I see out there.
public class Tag : ValueObject
{
public virtual User User { get; set; }
public virtual Question Question { get; set; }
public virtual string TagName { get; set; }
}
public class User
{
public virtual IList<Tag> Tags { get; set; }
}
public class Question
{
public virtual IList<Tag> Tags { get; set; }
}
Anyway, I am getting the following error:
{"The entity 'Tag' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)."}
I have the following Fluent NHibernate mapping for User and Question:
public void Override(AutoMapping<XXX> mapping)
{
mapping.HasMany(x => x.Tags).Component(c =>
{
c.Map(x => x.TagName);
c.Map(x => x.Question);
c.Map(x => x.User);
});
}
As always, any thought greatly appreciated.
Late Update: Okay, so, maybe this isn't a value object. It doesn't need an identity, but I guess it's not something that could be used in multiple places, either. Any way to handle this without forcing a useless Id field on my object?
Try this:
public void Override(AutoMapping<XXX> mapping)
{
mapping.HasMany(x => x.Tags).AsBag().Component(c =>
{
c.Map(x => x.TagName);
c.References(x => x.Question);
c.References(x => x.User);
});
}
but you cant query (list all) tags then because its a value object.