Working with interfaces and EF Fluent API - asp.net-mvc-3

Code
I will show you the code and then explain the problem
Interfaces
public interface IUser
{
Guid ID { get; set; }
string Name { get; set; }
ICollection<IRole> Roles { get; set; }
}
public interface IRole
{
Guid ID { get; set; }
string Name { get; set; }
}
Notice that the interface IUser define a collection Roles of type IRole
Implementation
public class Role : IRole
{
public Guid ID { get; set; }
public string Name { get; set; }
}
public class User : IUser
{
public Guid ID { get; set; }
public string Name { get; set; }
public ICollection<IRole> Roles { get; set; }
}
EF Fluent API Configuration
public class RoleConfiguration : EntityTypeConfiguration<Role>
{
public RoleConfiguration()
{
HasKey(p => p.ID)
.Property(p => p.ID);
Property(p => p.Name)
.IsRequired()
.HasMaxLength(70);
}
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasKey(p => p.ID)
.Property(p => p.ID)
.IsRequired();
Property(p => p.Name)
.HasMaxLength(60)
.IsRequired();
HasMany(r => r.Roles).WithMany();
}
}
Note that the configuration EntityTypeConfiguration where T is the implementation and not the interface (the EF does not allow to put the interface as T)
Problem
#1 situation:
If you run the application, to generate the relational model, the following error occurs:
The navigation property 'Roles' is not a declared property on type 'User'. Verify that it has not been explicitly excluded from the model and that it is a valid navigation property.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The navigation property 'Roles' is not a declared property on type 'User'. Verify that it has not been explicitly excluded from the model and that it is a valid navigation property.
Source Error:
Line 58: public IQueryable<Project> GetAll(int pageIndex, int pageSize, params Expression<Func<Project, object>>[] includeProperties)
Line 59: {
Line 60: return includeProperties.Aggregate<Expression<Func<Project, object>>,
Line 61: IQueryable<Project>>(Context.Projects, (current, includeProperty) => current.Include(includeProperty)).OrderBy(p => p.Name).Skip(pageIndex).Take(pageSize);
Line 62: }
#2 situation:
If you comment out the line HasMany(r => r.Roles).WithMany(); EF will generate the relational model with no relationship between User and Role (which should be many to many)
I believe this is because the User class, there is a collection type ICollection<IRole> and not of kind ICollection.
Question
The question is, how to solve this problem?
How to map the collection public ICollection<IRole> Roles { get; set; } using Fluent API EF

CodeFirst doesn't support mapping interfaces. You will need to change it to reference the Role concrete class.

Here's how I would implement your interfaces in order to work around the problem that Betty described.
public class Role : IRole
{
public Guid ID { get; set; }
public string Name { get; set; }
}
public class User : IUser
{
public Guid ID { get; set; }
public string Name { get; set; }
// Explicit implementation of the IUser.Roles property.
// Exists to satisfy the IUser interface but is not normally used.
// Used only when the client code types this object the interface, IUser.
// Otherwise ignored.
ICollection<IRole> IUser.Roles
{
get { return this.Roles as ICollection<IRole>; }
set { this.Roles = value as ICollection<Role>; }
}
// This property will be used in most cases.
// Used when the client code types this object as the concrete type, User.
public ICollection<Role> Roles { get; set; }
}

Related

Code first EF in ASP.NET core Web API single primary key

I'm using an external library to return data to me, the library has a lot of fields in it and goes quite deep with nested objects.
My class looks something like this;
public class Dto
{
public Dto(Val val)
{
Val = val;
}
[Key]
public int Id { get; set; }
public Val Value{ get; set; }
}
And when trying to save the changes to EF I get the error;
Cannot insert explicit value for identity column in table 'Value' when IDENTITY_INSERT is set to OFF.
When looking through the migrations it appears as if the Primary Key has been set on a few properties within the Value object. I've looked and looked but can't find a way to stop the migration automatically assigning primary keys. I've found stuff such as in the example above using the;
[Key]
attribute and adding bits and pieces into the OnModelCreating override but nothing has came up with a successful result.
Edit to give some more clarity instead of an example.
[DbContext(typeof(SummonerDtoContext))]
[Migration("20180314210242_Migration8")]
partial class Migration8
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.2-rtm-10011")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("LccWebAPI.Models.SummonerDto", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long?>("SummonerId");
b.HasKey("Id");
b.HasIndex("SummonerId");
b.ToTable("Summoners");
});
modelBuilder.Entity("RiotSharp.SummonerEndpoint.Summoner", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("AccountId");
b.Property<long>("Level");
b.Property<string>("Name");
b.Property<int>("ProfileIconId");
b.Property<int>("Region");
b.Property<DateTime>("RevisionDate");
b.HasKey("Id");
b.ToTable("Summoner");
});
modelBuilder.Entity("LccWebAPI.Models.SummonerDto", b =>
{
b.HasOne("RiotSharp.SummonerEndpoint.Summoner", "Summoner")
.WithMany()
.HasForeignKey("SummonerId");
});
#pragma warning restore 612, 618
}
}
}
and my actual data object I'm trying to store;
public class SummonerDto
{
public SummonerDto(Summoner summoner)
{
Summoner = summoner;
}
public int Id { get; set; }
public Summoner Summoner { get; set; }
}
and my context;
public class SummonerDtoContext : DbContext
{
public SummonerDtoContext(DbContextOptions<SummonerDtoContext> options)
: base(options)
{ }
public DbSet<SummonerDto> Summoners { get; set; }
}
I don't have access to modify the inside of the Summoner object itself to add annotations to ignore them as keys.
And the model structure of the Summoner object which I have no access to amend;
public class Summoner : SummonerBase
{
[JsonProperty("profileIconId")]
public int ProfileIconId { get; set; }
[JsonConverter(typeof(DateTimeConverterFromLong))]
[JsonProperty("revisionDate")]
public DateTime RevisionDate { get; set; }
[JsonProperty("summonerLevel")]
public long Level { get; set; }
}
And the base;
public class SummonerBase
{
public Region Region { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
public long AccountId { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
Edit : RESOLVED
After 3 hours of trying to figure this out I've finally managed to solve it, since I don't have access to annotate the models directly, I'd previously tried accessing the Ids through my SummonerDto model. Instead I accecssed them directly and it's worked.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SummonerDto>()
.HasKey(c => c.Id);
modelBuilder.Entity<Summoner>().Property(x => x.Id).ValueGeneratedNever();
modelBuilder.Entity<Summoner>().Property(x => x.ProfileIconId).ValueGeneratedNever();
modelBuilder.Entity<SummonerBase>().Property(x => x.Id).ValueGeneratedNever();
base.OnModelCreating(modelBuilder);
}

My Model creates an extra key for the database why?

Note: Technoligies in use are ASP.Net MVC 3, Entity, SQL Server Management Studio
Problem?
It seems that when I run, the context as: public class DatabaseInit : DropCreateDatabaseAlways<LocationAppContext>
That it creates the database, but my service assignments table has an extra foreign key called
ServiceAssignment_Service when it shouldn't.
My service assignment model is as such:
namespace LocationApp.Models
{
public class ServiceAssignment
{
public int id { get; set; }
public int locationID { get; set; }
public int ServiceID { get; set; }
public virtual Location Location { get; set; }
public virtual ServiceAssignment Service { get; set;}
}
}
and the service model is as such:
namespace LocationApp.Models
{
public class Service
{
public Service()
{
this.ServiceAssignments = new HashSet<ServiceAssignment>();
}
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public bool active { get; set; }
public string icon { get; set; }
public virtual ICollection<ServiceAssignment> ServiceAssignments { get; set; }
}
}
with that said, the relation ship is simple:
service assignments have many location id's and service id's.
why is this extra foriegn key being generated? the curent keys, that should e there is:
PK: Main PK for the table
FK 1: Location_ServiceAssignment
FK 2: Service_ServiceAssignment
Those are their, how ever this third one is baffling....
The second part is: If a location of id 2 has a service id of 2,3,6,7 How do I get all service id's returned, such that I can pass the object to a service query to get all information on the service based on the ID?
Update:
Context Class:
namespace LocationApp.DAL
{
public class LocationAppContext : DbContext
{
public DbSet<Content> Contents { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<ServiceAssignment> ServiceAssignments { get; set; }
public DbSet<Service> Services { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Location>().HasMany(sa => sa.ServiceAssignments);
modelBuilder.Entity<Service>().HasMany(sa => sa.ServiceAssignments);
}
}
}
I think you have to tell EF that Service.ServiceAssignments is the inverse navigation property of ServiceAssignment.Service and that Location.ServiceAssignments is the inverse of ServiceAssignment.Location. Right now with your mapping you only specify that Location or Service has many ServiceAssignments. EF will consider the navigation properties in ServiceAssignment as the ends of separate relationships.
Try instead the mapping:
modelBuilder.Entity<Location>()
.HasMany(l => l.ServiceAssignments)
.WithRequired(sa => sa.Location)
.HasForeignKey(sa => sa.LocationID);
modelBuilder.Entity<Service>()
.HasMany(s => s.ServiceAssignments)
.WithRequired(sa => sa.Service)
.HasForeignKey(sa => sa.ServiceID);
You can probably remove this mapping altogether as an alternative because EF should detect the right relationships by convention.
So, use either no mapping (=mapping by convention) or the full mapping (=specifying both ends of the relationships). Just the 50%-mapping you have used is likely the problem.

Convert a string value to an entity in Automapper

I am trying to figure out how to use Automapper when my entity has a field of type entity.
I've got 3 classes like these:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class Contact : Entity
{
public virtual string Name { get; set; }
public virtual Company Company { get; set; }
}
public class Company : Entity
{
public virtual string Name { get; set; }
}
My class Contact contain an element of type Company.
I've also created a ViewModel to trasfer some infos to my view:
public ContactViewModel()
{
public Guid Code { get; set; }
public int Version { get; set; }
[DisplayName("Contact")]
public string Name { get; set; }
[DisplayName("Company")]
public string Company { get; set; }
}
In my viewmodel I've defined a field Company of type string. This is going to contain a value the user will chose from a dropdown (list of companies).
I've defined a bootstrapper when my App starts:
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x => {
x.CreateMap<Domain.Contact, ViewModels.ContactViewModel>();
x.CreateMap<ViewModels.ContactViewModel, Domain.Contact>()
});
}
}
When I try to remap my ViewModel to my entity I get a conversion error (AutoMapper.AutoMapperMappingException).
Automapper can't figure out how to convert my Company (string) into an object of type Company, member of Contact.
Is it possible to define a rule so that Automapper know how to transform the string (company) into the code of my Company object, member of Contact?
You need to use a Resolver. Something like:
public class CompanyTypeResolver : ValueResolver<string, Company>
{
protected override Company ResolveCore(string name)
{
return new Company {Name = name};
}
}
Then in mapping code you call it like:
.ForMember(dto => dto.Company, opt => opt.ResolveUsing<CompanyTypeResolver>().FromMember(src => src.Name))

Linq return child entities not used in parent entities

I have the case whereby I have the following entities in my model.
public class Permission
{
public int ID { get; set; }
public Operation Operation { get; set; }
}
public class Operation
{
public int ID { get; set; }
public string Name { get; set; }
}
The way my repository is set up I need to query the OperationRepository to find all those operations that have not been used in a permission. My EF Operation Entity has a navigation property back to the Permissions as an EntityCollection as below:
public partial class Operation : EntityObject
{
public EntityCollection<Permission> Permissions
{
get; set;
}
}
The method in my OperationRepository is:
public IEnumerable<IOperation> FindUnassigned()
{
//query here
}
Filter your Operations where the navigation property `Permissions doesn't have any elements
.Where(p => p.Permissions.Count() == 0)

Fluent Nhibernate & Linq (Property Not Found)

I'm trying to get a web app working based on the S#arp Architecture. At the moment I have a the below code for my entity.
[Serializable]
public abstract class EventBase : Entity
{
#region Properties
[DomainSignature]
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual AgeRange Ages { get; set; }
public virtual int Rating { get; set; }
public virtual decimal Price { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual string EmailAddress { get; set; }
public virtual string Website { get; set; }
public virtual EventState State { get; set; }
#endregion
protected EventBase() {}
protected EventBase(string name, string description)
{
// ReSharper disable DoNotCallOverridableMethodsInConstructor
Name = name;
Description = description;
Price = 0;
State = EventState.New;
// ReSharper restore DoNotCallOverridableMethodsInConstructor
}
}
This is mapped using Fluent NHibernate as follows
public class EventBaseMap : AutoMap<EventBase>
{
public EventBaseMap()
{
Id(x => x.ID).WithUnsavedValue(-1).GeneratedBy.Identity();
Component<AgeRange>(x => x.Ages, m =>
{
m.Map(x => x.From).TheColumnNameIs("AgeFrom");
m.Map(x => x.To).TheColumnNameIs("AgeTo");
});
JoinedSubClass<Music>("EventId", sub =>
{
sub.Map(x => x.Headliner);
});
}
}
I created a very simple repository using the very useful S#arp base repository classes.
public interface IEventRepository : INHibernateRepositoryWithTypedId<EventBase, int>
{
List<EventBase> FindByName(string searchPhase);
}
public class EventRepository : NHibernateRepository<EventBase>, IEventRepository
{
public List<EventBase> FindByName(string searchPhase)
{
return Session.Linq<EventBase>().Where(x => x.Name == searchPhase).ToList();
}
}
I can create entities in the db and return all records. When I try to test the FindByName method I get the below error.
NHibernate.QueryException: could not
resolve property: Name of:
Model.Events.EventBase
Does anyone have any ideas? Is it a problem with my mapping?
Thanks,
This is using the Auto-mapping feature. I thought you only explicitly map properties that you want to override or that don't meet the conventions?
If I add an explicit mapping this solves the issue but I am still not sure why.

Resources