Use [PrimaryKey,AutoIncrement] from SQLite in .NET MAUI with [ObservablePropertys] - sqlite

As the Title says i defined my member, for example my Id, as an Observable Property with the CommunityToolKit.MVVM
[ObservableProperty]
private int id;
But now i am trying to give my Observable Property [PrimaryKey,AutoIncrement] from the SQLite Extension. But i cant just write it like that cause we have no self defined Get/Set only the generated one.
Is there a way to add that annotation while it still is an ObservableProperty?
I Imagine it something like that:
[ObservableProperty]
[PrimaryKey, AutoIncrement]
private int id;

No, you can't use the two attributes at the same time. The ObservableProperty need the property without get set method. But the sqlite need it. It's a conflict between them.
So you may need to use the codes such as:
[PrimaryKey, AutoIncrement]
private int id { get; set;}
public int Id
{
get => id;
set => SetProperty(ref id, value);
}

Related

Entity Frameworok generate guid as id and save it

I'm trying to save to my table Users let's say, string ID, string email, and string password. The problem is that ID must be a guid that I have to create it and save it and not SQL server. Any ideas how?
I searched but I only found how to make SQL server to create the guid.
First of all, tell Entity framework that you will generate the value of the primary key:
Use DatabaseGenerated Attribute
public class School
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name {get; set;}
...
}
The None option prevents values from being generated by the database automatically in cases where they would otherwise be created.
Furthermore, consider to overwrite DbContext.SaveChanges(). In this procedure ask the ChangeTracker for all elements that are Added. Generate an Id for every Added element. It might be dangerous to let others generate an Id, because they might be adding a constant value or just an auto-increment.
Another possibility would be to generate it within the Add function, but if you do that, then users could change your generated Id. So the proper place is within SaveChanges:
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
// This will fail: the added element doesn't have a property Id:
addedElement.Id = GenerateId();
}
return base.SaveChanges();
}
For this you have to be certain that every added element has a property Id. The simplest way is to create an interface and let all your tables implement this interface:
public interface IID
{
string Id {get; set;}
}
public class School : IID {...}
public class Student : IID {...}
public class Teacher : IID {...}
public class DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students{get; set;}
public DbSet<Teacher> Teachers {get; set;}
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries.Cast<IID>
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
addedElement.Id = GenerateId(); // Every Added element implements IId
}
return base.SaveChanges();
}
private string GenerateId()
{
... // TODO: return unique ID, for instance a GUID
}
}

Is it possible to set up this relationship via the Fluent API?

I have two code first entities, Package and PackageEntry that I am having trouble setting up in EF Core.
I am trying to achieve the following with the code first entities and the Fluent API:
A Package can contain any number of PackageEntries
Each PackageEntry has a reference to a single Package entity (a different instance of a package, unrelated to the parent Package reference that contains the collection of PackageEntries)
The two entities:
public class Package{
public Package()
{
_packageEntries = new List<PackageEntry>();
}
//trimmed other properties
private readonly List<PackageEntry> _packageEntries;
[NotMapped]
public IReadOnlyCollection<PackageEntry> PackageEntries => _packageEntries.ToList().AsReadOnly();
}
and
public class PackageEntry
{
public int DisplayOrder { get; set; }
public int PackageID { get; set; }
public Package Package { get; set; }
public int Quantity { get; set; }
public Package ParentPackage { get; set; }
public int ParentPackageID { get; set; }
}
What I currently have using the Fluent API, which is not working is:
modelBuilder.Entity<Package>().HasMany(x => x.PackageEntries).WithOne();
modelBuilder.Entity<PackageEntry>().HasOne(x => x.Package).WithOne().HasForeignKey(typeof(PackageEntry), "PackageID");
It isn't throwing errors, but what I am seeing is that when a PackageEntry is added to a package, it is not getting saved when calling SaveChanges on the context.
Am I doing something wrong with the Fluent API or something else?
EDIT
I had missed adding the top level package to the context, once that was done the package entry that gets added to it is being saved. I would still appreciate comments on the Fluent API setup and any best practices.
From the PackageEntry entity, I need to know both the Parent Package and the contained Package which will be separate references to the same type. I can't seem to set this up with the Fluent API, when the Parent Package is loaded via EF it doesn't contain any PackageEntry objects, even if their ParentPackageID is set correctly.
Upon some offline advice from an EF expert, I have worked around this issue by removing the navigation property for PackageEntry.Package and simply manually handle the foreign key for that package entity.
Once I did that, now when the Parent Package entity is loaded, it properly loads the children PackageEntries.
So, the PackageEntry class now looks like this:
public class PackageEntry
{
public int DisplayOrder { get; set; }
public int PackageID { get; set; }
//public Package Package { get; set; } //Handle manually
public int Quantity { get; set; }
public Package ParentPackage { get; set; }
public int ParentPackageID { get; set; }
}
And the Fluent API code:
navigation = builder.Metadata.FindNavigation(nameof(Package.PackageEntries));
//EF access the PackageEntries collection property through its backing field
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
modelBuilder.Entity<Package>().HasMany(x => x.PackageEntries)
.WithOne("ParentPackage")
.HasForeignKey(nameof(PackageEntry.ParentPackageID))
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
Your Package.PackageEntries collection is marked [NotMapped], and it does not have a setter. No matter what, EntityFramework is not going to pick that up.
I've never tried using an IReadonlyCollection<T> with EntityFramework, but I would imagine that EF won't like that either.
Your first try should be to remove the attribute and arrange the property like this:
public virtual IReadOnlyCollection<PackageEntry> PackageEntries {
get {
return _packageEntries.ToList().AsReadonly();
}
protected internal set {
_packageEntries = value;
}
}
Granted, that would require you to remove the readonly from the private member variable.
That being said, I'm not sure if EF has an internal list that it eventually assigns to the property, but I would imagine that it would just call the Add() method on the collection (which is why your properties must be ICollection<T> instead of IEnumerable<T>.
Therefore, if that is all still not working, you should make _packageEntries protected internal and use that as your EF collection. Then you can only publicly expose your PackageEntries as you are doing now.

Entity Framework making incorrect PK-FK mapping on Code First migration

I have the following 3 classes set up to be created in a SQL Server database using Entity Framework Code First migrations. The Survey object is the main table.
public class Survey
{
public int SurveyId {get; set;} //Primary Key
public string Description {get; set;}
public bool HasDevice {get; set;}
public bool HasProcess {get; set;}
public virtual Process Process {get; set;}
public virtual ICollection<Device> Devices {get; set;}
}
Each Survey can have multiple Devices (1-to-many)
public class Device
{
public int DeviceId {get; set;} //Primary Key
public string DeviceType {get; set;}
public int SurveyId {get; set;} //Foreign Key
public virtual Survey Survey {get; set;}
}
Each Survey should have only one Process (1-to-0..1)
public class Process
{
public int ProcessId {get; set;} //Primary Key
public string ProcessInfo {get; set;}
public int SurveyId {get; set;} //Foreign Key
public virtual Survey Survey {get; set;}
}
The Fluent API mapping for these classes looks like this.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("Survey");
modelBuilder.Entity<Survey>().HasOptional(x => x.Process).WithRequired(x => x.Survey);
modelBuilder.Entity<Survey>().HasMany(x => x.Devices).WithRequired(x => x.Survey);
}
The problem is that when I apply the code first migration, the ForeignKey property in the Process table (1-to-0..1) keeps getting set to the ProcessId field rather than the SurveyId. This means that every time I try to add a new Process record, I get the following error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Survey.Processes_Survey.Surveys_ProcessId". The conflict occurred in database "Backflow", table "Survey.Surveys", column 'SurveyId'.
The 1-to-many mapping for Device works just fine.
I thought initially that this was due to having all my PK fields just say Id, but even after adding in the additional label part, it still makes the incorrect PK-FK link. I have also tried avoiding the Fluent API by adding the DataAnnotation [Key, ForeignKey("xyz")] instead but it has the same result. Recompiling the project, restarting Visual Studio, and even creating a new project and a new database do not help.
Is there something in the Fluent API or DataAnnotations that I am missing to get this to join correctly? Also, manually fixing the FK in the database does make it work, but that kind of defeats the purpose of doing everything in Code First with migrations.
The fluent mapping of the 1-0..1 relationship is correct:
modelBuilder.Entity<Survey>()
.HasOptional(s => s.Process)
.WithRequired(p => p.Survey);
But Process shouldn't have a SurveyID property (and column). In EF6, the dependent part of a 1-0..1 relationship (here: Process) is supposed to have a primary key that also refers to its principal (here: Survey) as foreign key. So Process.ProcessID is both primary key and foreign key. Thus, one Process is uniquely tied to one Survey.
By the way, in the other mapping, I would also mention the foreign key: if configuration is chosen over convention, it better be complete.
modelBuilder.Entity<Survey>()
.HasMany(s => s.Devices)
.WithRequired(d => d.Survey)
.HasForeignKey(d => d.SurveyId);

No key defined. Define the key for this EntityType when implementing interface that has Id

I want to implement an interface defined in Dev Express IResource in my EF Code First business object.
public class JResource : IResource , IXafEntityObject
{
[Key]
public int IdKey { get; set; }
[NotMapped]
public object Id => IdKey; // since IResource wants this.
// other properties
}
When I run my application to create the database I get an error that I need to define the key for this EntityType.
I think the problem is that EF wants to regard Id as the Key but I have made Id NotMapped since I want it to be an Int and the interface wants it to be an object.
Is there a work around?
It turned out that I was using the wrong reference for the [Key] attribute.
The correct one is
System.ComponentModel.DataAnnotations.KeyAttribute

How to sort data in an asp.net gridview by properties of sub-objects?

I have a List<Role> (see below) that I am binding to an asp.net gridview. I want to sort this data using SortExpression, such that it is sorted by two properties of sub-objects of the rows. Specifically, I want to sort by the Application's Name, then the ApplicationType's ApplicationTypeName.
How can I do this?
The classes here are:
public class Application
{
public string Name {get; set;}
public int Status {get; set;}
}
public class ApplicationType
{
public string ApplicationTypeName {get; set;}
public int ApplicationTypeStatus {get; set;}
}
public class Role
{
public Application oApplication {get; set;}
public ApplicationType oApplicationType {get; set;}
}
Edit: note that I was responding to the earlier verison of the question, before it related to gridview; still, this might be useful...
Worst case: you can use the approach here to pre-sort the list before binding it to the gridview.
Various options:
implement IComparable[<T>]
implement IComparer[<T>]
use an ad-hoc sort
I'm guessing you just need the last, so perhaps:
list.Sort((x,y) => {
int delta = string.Compare(x.Application.Name, y.Application.Name);
if (delta == 0) delta = string.Compare(
x.ApplicationType.ApplicationTypeName, y.ApplicationType.ApplicationTypeName);
return delta;
});
Alternatively, you can perhaps do it via LINQ in the source data - note however that this is done when creating a new list - it isn't an in-place sort of an existing list:
var list = source.OrderBy(x => x.Application.Name)
.ThenBy(x => x.ApplicationType.ApplicationTypeName)
.ToList();

Resources