Code first EF in ASP.NET core Web API single primary key - asp.net-core-mvc

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);
}

Related

FluentValidation Set Valid Result to a Custom Property

I am validating the content for file import and I have an IsValid property for each line.
public class Header
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public bool IsValid { get; set; }
}
public class Detail
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public bool IsValid { get; set; }
}
public class Trailer
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public bool IsValid { get; set; }
}
public class ImportFile
{
public Header Header { get; set; }
public List<Detail> Details { get; set; }
public Trailer Trailer { get; set; }
}
and my validators look somewhat like:
public class DetailValidator : AbstractValidator<Detail>
{
public DetailValidator()
{
RuleFor(d => d.Property1)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.Length(3)
.WithState(d => d.LineNumber);
RuleFor(d => d.Property2)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.MaximumLength(50)
.WithState(d => d.LineNumber);
...
}
}
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.SetValidator(new HeaderValidator());
RuleForEach(f => f.Details)
.SetValidator(new DetailsValidator());
...
}
}
After I call the validation, I wanted to set the IsValid property of each line of the file (be it header, detail or trailer) base from the result of the validation.
What is possible for now is, since I am using WithState to store the LineNumber, I can match the ValidationResult against the ImportFile instance to set each line's validity like below:
ImportFile file = // parsed file content
var result = new ImportFileValidator().Validate(file);
foreach (var detail in file.Details)
{
var error = result.Errors.FirstOrDefault(e =>
Convert.ToInt32(e.CustomState) == detail.LineNumber);
detail.IsValid = error == null;
}
And I have to check for the header and trailer as well.
Is there a way I can do this inside the validators? I am trying to explore the FluentValidation's documentation, but I can't seem to find what I needed there.
As I was exploring the available methods in FluentValidation, I saw OnFailure and OnAnyFailure methods. This methods might be a good help to what I needed to do, but the problem is they're obsolete as of 10.3.0 and will be removed on version 11. They're suggesting to use a custom validator instead.
The Header, Detail and Trailer Abstract Validators remain as is.
I created custom validator extensions for those 3.
Each extension methods creates an instance of the corresponding validator and executes it. I can make them generic for header, detail and trailer since they will do the same thing, set IsValid property to the validation result.
public static IRuleBuilderOptionsConditions<ImportFile, T> IsHeaderValid<T>(this IRuleBuilder<ImportFile, T> ruleBuilder)
where T : Header
{
return builder.Custom((header, context) =>
{
// Create the Header Abstract Validator Instance
var validator = new HeaderValidator();
var result = validator.Validate(Header);
header.IsValid = result.IsValid;
// Pass the errors to the context
result.Errors.ForEach(context.AddFailure);
}
}
I had to change the ImportFileValidator to call the custom validators, instead of using setvalidator.
The ImportFileValidator looks like this:
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.IsHeaderValid();
RuleForEach(f => f.Details)
.IsDetailValid();
...
}
}
This is pretty much how I was able to set the IsValid property without having to do the matching I initially did in the question.

Automapper unable to project one enum type to another

I'm leveraging the Project functionality in Automapper and Entity Framework, but I'm running into an issue where Automapper doesn't seem to want to project one enum type to another.
I have the following entities:
public class UserProfile
{
public Guid Id { get; set; }
public string Name { get; set; }
private HashSet<UserProfilePhone> m_Phones;
public virtual HashSet<UserProfilePhone> Phones
{
get { return m_Phones ?? (m_Phones = new HashSet<UserProfilePhone>()); }
set { this.m_Phones = value; }
}
}
public class UserProfilePhone
{
public PhoneType Type { get; set; }
public virtual string Number { get; set; }
}
public enum PhoneType
{
Home = 1,
Work = 2,
Mobile = 3,
Other = 4
}
I then am projecting these types to the following models:
public class UserProfileModel
{
public Guid Id { get; set; }
public virtual string Name { get; set; }
public IEnumerable<UserProfilePhoneModel> Phones { get; set; }
}
public class UserProfilePhoneModel
{
public UserProfilePhoneTypeModel Type { get; set; }
public string Number { get; set; }
}
public enum UserProfilePhoneTypeModel
{
Home = 1,
Work = 2,
Mobile = 3,
Other = 4
}
I then setup my mappings like so:
Mapper.CreateMap<PhoneType, UserProfilePhoneTypeModel>();
Mapper.CreateMap<UserProfilePhone, UserProfilePhoneModel>();
Mapper.CreateMap<UserProfile, UserProfileModel>();
And finally I'm executing my projection:
var result = dbContext.UserProfiles.Project().To<UserProfileModel>();
When I do this, I get the following exception:
AutoMapper.AutoMapperMappingException: Unable to create a map expression from MyNamespace.PhoneType to
MyNamespace.Models.UserProfilePhoneTypeModel
Unable to create a map expression from MyNamespace.PhoneType to MyNamespace.Models.UserProfilePhoneTypeModel
Result StackTrace:
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey
key, Func2 valueFactory)
...
I've tried creating explicit mappings, but they appear to be ignored. What am I doing wrong here?
As usual, I figured out the answer almost as soon as I posted the question.
Modifying the create map line to provide an explicit cast did the trick:
Mapper.CreateMap<UserProfilePhone, UserProfilePhoneModel>()
.ForMember(m => m.Type, opt => opt.MapFrom(t => (UserProfilePhoneTypeModel)t.Type));

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.

Specified member type is not supported linq

Can anyone tell me what kind of error is this?
The specified type member 'OrderDetails' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
What's wrong here in this code?
return storeDB.Albums.OrderByDescending(a=>a.OrderDetails.Count()).Take(count).ToList()‌​
Since you are using EF code first try to add a configuration class to map the one to many relationship between Album and OrderDetails. The following will be a sample configuration for the Album.
public class AlbumConfiguration : EntityTypeConfiguration<Album>
{
public AlbumConfiguration()
{
HasKey(a => a.Id);
HasMany(album => album.Orders).WithOptional(order => order.Album).
HasForeignKey(order => order.AlbumId);
}
}
and your OrderDetails should be changed as follows
OrderDetail
{
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public int AlbumId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public virtual Album Album { get; set; }
public virtual Order Order { get; set; }
}
and your Album class should have a virtual List of OrderDetails.
and finally in your DbContext class add the configuration by overrinding the OnModelCreating method. sample class would be
public class YourContext : DbContext
{
// your DBSets and contructors, etc
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AlbumConfiguration());
base.OnModelCreating(modelBuilder);
}
}
Try this may be this is the cause of your exception

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