ef core 5 using Enumeration as composite keys - asp.net-core

I'm trying to get benefit from eshoponcontainers code to use classes derived from Enumeration class to address it like CardType.Visa. I think it's very convenient.
But when i try to set this type as composite key it doesn't work.
I use ef core 5 with .net core 5.
Please, any suggestions.
public class FakeEntity: BaseEntity
{
public string Name { get; set; }
public int ContractId { get; set; }
}
public class FakeEntityLink
{
public FakeEntity FakeEntity { get; set; }
public int FakeEntityId { get; set; }
public int BillParamTypeEnumId { get; set; }
public BillParamTypeEnum BillParamTypeEnum { get; private set; } = BillParamTypeEnum.PriceCategory;
private FakeEntityLink() { }
public FakeEntityLink(FakeEntity fakeEntity, BillParamTypeEnum billParamTypeEnum)
{
FakeEntity = fakeEntity;
BillParamTypeEnum = billParamTypeEnum;
}
}
public class BillParamTypeEnum
: Enumeration
{
public static BillParamTypeEnum PriceCategory => new BillParamTypeEnum(1,"PriceCategory ");
public static BillParamTypeEnum VoltageTarifLevel => new BillParamTypeEnum(2, "VoltageTarifLevel ");
public static BillParamTypeEnum Sign => new BillParamTypeEnum(3,"Sign ");
public static BillParamTypeEnum VolumeCategory => new BillParamTypeEnum(4,"VolumeCategory");
public BillParamTypeEnum(int id, string name)
: base(id, name)
{
}
}
//and in dbContext
modelBuilder.Entity<FakeEntityLink>(entity => {
entity.HasKey(c => new { c.FakeEntityId, c.BillParamTypeEnumId });
entity.HasOne(link => link.FakeEntity).WithMany();
entity.HasOne(link => link.BillParamTypeEnum).WithMany().HasForeignKey(p=>p.BillParamTypeEnumId);
}
);
// code in controller
var fe =_db.FakeEntities.Find(1);
_db.FakeEntityLinks.Add(new FakeEntityLink(fe, BillParamTypeEnum.VoltageTarifLevel));
_db.SaveChanges(); // throws SqlException: Cannot insert explicit value for identity column in table 'BillParamTypeEnum' when IDENTITY_INSERT is set to OFF.

Related

How to update an existing entity that has a nested list of entities?

I'm trying to update an entity using entity framework but, everytime I try to do it, it raises an error saying that a nested entity the main class contains cannot be tracked.
These are my classes:
public abstract class BaseEntity
{
public int Id { get; set; }
}
public class Dashboard : BaseEntity
{
public int Order { get; set; }
public string Title { get; set; }
public bool Enabled { get; set; }
public virtual ICollection<Submenu> Submenu { get; set; }
}
public class Submenu : BaseEntity
{
public int Order { get; set; }
public bool Enabled { get; set; }
public string Title { get; set; }
public string Image { get; set; }
public string Descriptions { get; set; }
public virtual ICollection<Action> Actions { get; set; }
public int DashboardId { get; set; }
public virtual Dashboard Dashboard { get; set; }
}
public class Action : BaseEntity
{
public string Type { get; set; }
public string Label { get; set; }
public string Url { get; set; }
public string Extension { get; set; }
public virtual Submenu Submenu { get; set; }
public int SubmenuId { get; set; }
}
The one I am using to update is Dashboard, which contains the rest of the classes.
I'm trying to do it using a generic service layer and a generic repository that are defined this way:
public class GenericService<T> : IGenericService<T> where T : BaseEntity
{
private readonly IBaseRepository<T> baseRepository;
public GenericService(IBaseRepository<T> baseRepository)
{
this.baseRepository = baseRepository;
}
public async Task Update(T entity, T attachedEntity)
{
await baseRepository.Update(entity, attachedEntity);
}
}
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly PortalContext dataContext;
private DbSet<T> DbSet { get; set; }
public BaseRepository(PortalContext context)
{
dataContext = context;
DbSet = dataContext.Set<T>();
}
public async Task Update(T entity, T attachedEntity)
{
dataContext.Entry(attachedEntity).State = EntityState.Detached;
DbSet.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
await dataContext.SaveChangesAsync();
}
}
And, at last but no least, this is the way I am configuring everything at Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PortalContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("PortalContext"))
);
services.AddTransient(typeof(IGenericService<>), typeof(GenericService<>));
services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddTransient<Func<string, ClaimsPrincipal, IRoleCheck>>((serviceProvider) =>
{
return (controllerName, claimsPrincipal) =>
new RoleCheck(serviceProvider.GetRequiredService<IGenericService<Dossier>>(),
serviceProvider.GetRequiredService<IGenericService<DossierTemplate>>(),
serviceProvider.GetRequiredService<IGenericService<Dashboard>>(),
controllerName, claimsPrincipal);
});
}
What the application first does is calling the RoleCheck class to retrieve and filter the required entities and, after that, the user can update them.
When I call the update function at the controller
public async Task<ActionResult<Dashboard>> Put(int id, [FromBody] Dashboard dashboard)
{
var currentDashboard = await service.Get(id);
if (currentDashboard == null)
{
return NotFound();
}
await service.Update(dashboard, currentDashboard);
return Ok();
}
I always receive the next error at the repository:
error
Is there something I am doing wrong? I have been stuck with this for a week now...
Thanks in advance and sorry for the long text, but I wanted it to be clear.
I could finally solve it by adding .AsNoTracking() at the Get() method of my repository:
public async Task<T> Get(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> includes)
{
IQueryable <T> query = DbSet.AsNoTracking();
if (includes != null)
{
query = includes(query);
}
return await query.FirstOrDefaultAsync(m => m.Id == id);
}

ef core may to many update with extra filed

My mode class:
public class AccountCustomer
{
public bool IsMain { get; set; }
public int AccountId { get; set; }
public Account Account { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
public class Account
{
public int Id { get; set; }
public strig No{ get; set; }
..other fields
public ICollection<AccountCustomer> AccCustomer { get; set; } = new List<AccountCustomer>();
}
public class Customer
{
public int Id { get; set; }
public strig Name{ get; set; }
..other fields
public ICollection<AccountCustomer> AccCustomer { get; set; } = new List<AccountCustomer>();
}
I found soluton here and i have implemented same later i found that it is for without extra column
Many to many ef core updaate
Please let meknow how to do update... Am using latest ef 5 preview
My code :
_context.Set<AccountCustomer>().UpdateLinks(ac => ac.AccountId, account.Id,
ac => ac.CustomerId, account.AccCustomer .Select(ac => ac.CustomerId));
Codes of ManyToMany Update Extensions
Remove the unselected item and add new item to list.
public static class Extensions
{
public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
{
db.Set<T>().RemoveRange(currentItems.ExceptThat(newItems, getKey));
db.Set<T>().AddRange(newItems.ExceptThat(currentItems, getKey));
}
private static IEnumerable<T> ExceptThat<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
{
return items
.GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
.SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
.Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
.Select(t => t.t.item);
}
}
Codes of Update ViewModel
The update view pass it to action via request.
public class AccountCustomerVM
{
public bool IsMain { get; set; }
public Account Account { get; set; }
public List<int> Customers { get; set; }
}
Codes of Update Action
[HttpPut]
public IActionResult Update(AccountCustomerVM accountCustomerVM)
{
var model = _context.Accounts.Include(x => x.AccCustomer).FirstOrDefault(x => x.Id == accountCustomerVM.Account.Id);
_context.TryUpdateManyToMany(model.AccCustomer, accountCustomerVM.Customers
.Select(x => new AccountCustomer
{
IsMain = accountCustomerVM.IsMain,
CustomerId = x,
AccountId = accountCustomerVM.Account.Id
}), x => x.CustomerId);
_context.SaveChanges();
return Ok();
}

select - keyword not working with odata, automapper and efcore

I am trying to apply the odata query to my automapper - mappings at my efcore context. Everything works as expected until I use the $select query option.
When I try to use the select keyword in the request to my odata - controller, I get the exception:
SerializationException: 'SourceSourceInjectedQuery`2' cannot be serialized using the ODataMediaTypeFormatter.
I am using the UseAsDataSource - Extension method because it was recommended here on github.
This is my oDataController:
public class StudentsController : ODataController {
private readonly SchoolContext schoolContext;
public StudentsController(SchoolContext schoolContext) {
this.schoolContext = schoolContext;
}
[EnableQuery]
public IActionResult Get() {
return Ok(
schoolContext
.Students
.UseAsDataSource()
.For<StudentVM>()
);
}
}
This is my Entity for EFCore:
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
And this is my mappingprofile for automapper:
public class StudentVM {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
}
public class StudentProfile : Profile {
public StudentProfile() {
CreateMap<Student, StudentVM>();
}
}
Do I need some specific mapping to do this?
I figured out I had a mistake in my configuration of the odataservice inside my startup.cs
private static IEdmModel GetEdmModel() {
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Student>("Students");
builder.EntitySet<Course>("Courses");
return builder.GetEdmModel();
}
I put my Entities instead of my ViewModels there. This is the fixed code:
private static IEdmModel GetEdmModel() {
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<StudentVM>("Students");
builder.EntitySet<CourseVM>("Courses");
return builder.GetEdmModel();
}
Now it's working as expected

mapping one-to-many relation with Fluent NHibernate does not contain any child when browsing through one-end

I've created the following domain classes:
public class Car
{
public virtual int Id { get; set; }
public virtual string Registration { get; set; }
public virtual User ResponsibleContact { get; set; }
protected Car()
{}
public Fahrzeug(User responsibleContact, string registration)
{
ResponsibleContact = responsibleContact;
Registration = registration;
ResponsibleContact.Cars.Add(this);
}
}
public class User
{
public virtual int Id { get; set; }
public virtual byte[] EncryptedUsername { get; set; }
public virtual byte[] EncryptedPassword { get; set; }
public virtual IList<Car> Cars { get; private set; }
public virtual string Username
{
get
{
var decrypter = UnityContainerProvider.GetInstance().UnityContainer.Resolve<IRijndaelCrypting>();
return decrypter.DecryptString(EncryptedUsername);
}
}
protected User()
{ }
public User(byte[] encryptedUser, byte[] encryptedPassword)
{
Cars = new List<Car>();
EncryptedUsername = encryptedUser;
EncryptedPassword = encryptedPassword;
}
}
and the mapping classes:
public class CarMap : ClassMap<Car>
{
public CarMap()
{
Id(c => c.Id).GeneratedBy.Native();
Map(c => c.Registration);
References(c => c.ResponsibleContact).Not.Nullable();
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(st => st.Id).GeneratedBy.Native();
Map(st => st.EncryptedUsername).Column("Username");
Map(st => st.EncryptedPassword).Column("Password");
HasMany(st => st.Cars).Inverse().AsBag();
}
}
If I query some Member objects, I get the members, but the cars collection is empty!
If I query some Cars I got all the cars with the right Member. But within the member, the cars collection is also empty!
Is there anybody who has an Idea of what can happened?
you have to make sure the foreign key column of the collection and the reference is the same otherwise there is a mismatch.
References(c => c.ResponsibleContact, "ResponsibleContact_id").Not.Nullable();
and
HasMany(st => st.Cars).Inverse().KeyColumn("ResponsibleContact_id");

Sharp Architecture issue with EntityWithTypedId

I wish to use Guid comb for my identity. I've added the EntityWithTypedId interface to my class which cause my table not to persist. ie using nh prof i can see the SaveOrUpdate method is not called. If i just use the Entity interface i can see it in the profiler.
public class Application : EntityWithTypedId<Guid>
{
public virtual string Name { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual string ModifiedByUserName { get; set; }
public virtual DateTime Created { get; set; }
public virtual DateTime Modified { get; set; }
}
public class ApplicationQuery : NHibernateQuery, IApplicationQuery
{
public void Update(Application application)
{
Session.SaveOrUpdate(application);
}
}
public class ApplicationMap : IAutoMappingOverride<Application>
{
public void Override(AutoMapping<Application> mapping)
{
mapping.Table("Application");
mapping.Id(x => x.Id, "Id").GeneratedBy.GuidComb();
}
}
Controller
[HttpPost]
[ActionName("Edit")]
public ActionResult EditPost(Application application)
{
var updateApplication = new Application();
updateApplication.Created = DateTime.Now;
updateApplication.Modified = DateTime.Now;
_applicationQuery.Update(updateApplication);
return RedirectToAction("Index");
}
I fixed this by updating my mapping to the following.
public void Override(AutoMapping<Application> mapping)
{
mapping.Table("Application");
mapping.Id(x => x.Id, "Id").GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
}
See here for more info.
http://s274.codeinspot.com/q/1486941