Entity Framework adding item to ICollection error - c#

I have two simple classes, User and Task:
class User
{
public int UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
class Task
{
public int TaskId { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public virtual ICollection<User> Followers { get; set; }
}
The Task class has a property Followers which is an ICollection<User>
Here is the db context class:
class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Task> Tasks { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<User>().HasKey(u => u.UserId);
mb.Entity<Task>().HasKey(t => t.TaskId);
}
}
and here is the code in the Main program:
var db = new MyContext();
var user = new User();
user.Name = "John Doe";
user.Email = "jd#email.com";
db.Users.Add(user);
db.SaveChanges();
var follower = db.Users.Where(u => u.Name == "John Doe").FirstOrDefault();
var task = new Task();
task.Name = "Make the tea";
task.CreatedAt = DateTime.Now;
task.Followers.Add(follower); // ERROR
db.Tasks.Add(task);
db.SaveChanges();
The trouble is I am getting an error when trying to add the follower to the task.
Object reference not set to an instance of an object.
What am I doing wrong?

The problem is that the Followers collection is null. Instead of newing up your classes, let EF create them for you ...
var user = db.Users.Create();
and
var task = db.Tasks.Create();
If you're still getting problems then your proxies are not being created. You can initialise the collections in the class constructors, make each of them a HashSet<T>. It would be better though to identify why the proxies are not getting generated ...
public class Task
{
public Task()
{
Followers = new HashSet<User>();
}
public int TaskId { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public virtual ICollection<User> Followers { get; set; }
}

try this. just initialize Follower
var db = new MyContext();
var user = new User();
user.Name = "John Doe";
user.Email = "jd#email.com";
db.Users.Add(user);
db.SaveChanges();
var follower = db.Users.Where(u => u.Name == "John Doe").FirstOrDefault();
var task = new Task();
task.Name = "Make the tea";
task.CreatedAt = DateTime.Now;
task.Followers = new Collection<User>()
task.Followers.Add(follower);
db.Tasks.Add(task);
db.SaveChanges();

Try this. You will have to chan ge the constructor as mentioned by qujck
var db = new MyContext();
var user = new User();
user.Name = "John Doe";
user.Email = "jd#email.com";
var task = new Task();
task.Name = "Make the tea";
task.CreatedAt = DateTime.Now;
task.Followers.Add(user);
db.Tasks.Add(task);
db.SaveChanges();

You can initialize the List, because ICollection is an interface then it can't be initialized, but List can be (the following worked for me)
Instead of:
task.Followers.Add(follower);
Write:
task.Followers= new List<User>();
task.Followers.Add(follower);
This should solve your problem :)

Related

Identity: How to add items to related fields

I want realise edit form, but error update item.
This is small example.
public class AppUser : IdentityUser
{
public string SurName { get; set; }
public virtual ICollection<City> Cities{ get; set; }
public AppUser()
{
Cities = new List<City>();
}
}
And the a model for the City
public class City
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AppUser> Users { get; set; }
public City()
{
Users = new List<AppUser>();
}
}
And the UserContext class building up the one-to-many relationship between the 2 models.
public class UserContext: IdentityDbContext<AppUser>
{
public DbSet<City> Cities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<City>().HasMany(x => x.Users)
.WithMany(x => x.Cities)
.Map(x => x.MapLeftKey("CityId")
.MapRightKey("UserId")
.ToTable("UserCity"));
base.OnModelCreating(modelBuilder);
}
}
And if I try update
UserContext db = new UserContext();
....
public ActionResult Edit()
{
var newUser = UserManager.FindById(User.Identity.GetUserId());
City city = db.Cities.FirstOrDefault();
newUser.Cities = new List<City>() { city };
newUser.SurName = "TestName";
var result = UserManager.Update(newUser);
....
}
I see error
System.InvalidOperationException:
"The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects."
If delete line
newUser.Cities = new List<City>() { city };
Error not show.
Why an error occurs when adding a field with many-to-many relationships?
solution: change from
var newUser = UserManager.FindById(User.Identity.GetUserId());
to
var newUser = db.Users.Include(x=>x.Cities).FirstOrDefault(x=>x.Id== userId);
Full list code change
public ActionResult Edit()
{
//var newUser = UserManager.FindById(User.Identity.GetUserId());
City city = db.Cities.FirstOrDefault();
newUser.Cities = new List<City>() { city };
newUser.SurName = "TestName";
//var result = UserManager.Update(newUser);
db.Entry(newUser).State = EntityState.Modified;
db.SaveChanges();
....
}

Behavior of relations in EF Core, concurrency of when objects are created

I've come across a problem with Entity framework Core 2.0.2 and I wonder if someone can enlighten me.
What I have is a person class with a collection of phone numbers.
Person
public class Person : BaseEntity
{
[Required]
public string FirstName { get; set; }
public string MiddleName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Context { get; set; }
public string Email { get; set; }
public ICollection<PhoneNumber> PhoneNumbers { get; set; } = new List<PhoneNumber>();
}
PhoneNumber
public class PhoneNumber : BaseEntity
{
public string Usage { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
}
BaseEntity is basically just a property with an id.
Then I have a generic repo
Repository
public class Repository<T> where T : BaseEntity
{
private readonly Dab2_2RdbContext _context;
public Repository(Dab2_2RdbContext context)
{
_context = context;
}
public void Create(T t)
{
_context.Entry<T>(t).State = EntityState.Added;
_context.SaveChanges();
}
public T Read(int id)
{
return _context.Find<T>(id);
}
public void Update(int id, T t)
{
_context.Entry<T>(t).State = EntityState.Modified;
_context.SaveChanges();
}
public void Delete(T t)
{
_context.Entry<T>(t).State = EntityState.Deleted;
_context.SaveChanges();
}
}
All of this code is very simple and works fine.
The situation happens when I test the code like this:
var context = new Dab2_2RdbContext();
var personRepo = new Repository<Person>(context);
var phoneNumberRepo = new Repository<PhoneNumber>(context);
var person = new Person()
{
FirstName = "Kasper",
LastName = "Lastname",
Email = "Something#gmail.com",
Context = "Myself",
};
person.PhoneNumbers = new List<PhoneNumber>()
{
new PhoneNumber() {Usage = "Work"},
new PhoneNumber() {Usage = "School"}
};
// Create
personRepo.Create(person);
This code generates a person with proper values, however, it doesn't include the Phone numbers.
var context = new Dab2_2RdbContext();
var personRepo = new Repository<Person>(context);
var phoneNumberRepo = new Repository<PhoneNumber>(context);
var person = new Person()
{
FirstName = "Kasper",
LastName = "Lastname",
Email = "Something#gmail.com",
Context = "Myself",
};
// Create
personRepo.Create(person);
person.PhoneNumbers = new List<PhoneNumber>()
{
new PhoneNumber() {Usage = "Work"},
new PhoneNumber() {Usage = "School"}
};
personRepo.Update(person.Id, person);
This code makes the proper relationship (the person.The phonenumber relation has been made. Notice the // Create person, shifted above person.Phone number
I wonder if someone can enlighten me, I really can figure out why this is.
The answer is that setting the state of the entry. Will only track the entity, and none of it's properties. While add will track all the navigation properties.

How to get List of item using join in LINQ?

I am working on an existing solution. Where I have two entities like
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime? DOB { get; set; }
private List<long> _accounts = new List<long>();
[Display(Name = "Account No")]
public List<long> Accounts
{
get { return _accounts; }
set { _accounts = value; }
}
[Display(Name = "Account No")]
public string AccountId
{
get { return string.Join(",", _accounts); }
set { _accounts = value != null ? value.Split(',').Where(s=>!string.IsNullOrWhiteSpace(s)).Select(s => Convert.ToInt64(s.Trim())).ToList() : new List<long>(); }
}
}
public class Account
{
public long Id { get; set; }
public string AccountName { get; set; }
public string AccountNo { get; set; }
}
and also have one ViewModel
public class UserAccountViewModel
{
public long AccountId { get; set; }
public string AccountNo { get; set; }
public int UserId { get; set; }
public string UserName { get; set; }
}
Now, how can I get List<UserAccountViewModel> from those entities?
var UserAccountViewModel = (from Account in list1
join UserAccountViewModel in list2
on Account .AccountNo equals UserAccountViewModel.AccountNo
select new { UserAccountViewModel }).ToList();
Hope it helps you.
Or to increase the performance as per suggestion
var query = dataContext.UserAccountViewModel
.Include(o => o.Account)
.Where(t=> t.AccountNo == t.Account.AccountNo).ToList();
If you have two lists of accnouts and users from which you want to create List, you can use:
var userAccount = (from account in list1
join user in list2
on account.Id.ToString() equals user.AccountIds
select new UserAccountViewModel
{
AccountId = account.Id,
AccountNo = account.AccountNo,
UserName = user.Name,
UserId = user.Id
}).ToList();
At last I get my answer.It's something like below example:
var users = new List<User>{
new User{Id=1 ,Name="Test1",Email="test1#ds.com",DOB=DateTime.Now,AccountId ="1"},
new User{Id=2 ,Name="Test2",Email="test2#ds.com",DOB=DateTime.Now,AccountId ="2"},
new User{Id=3 ,Name="Test3",Email="test3#ds.com",DOB=DateTime.Now,AccountId ="3,4"}
};
var accunts = new List<Account>
{
new Account {Id=1,AccountName = "A",AccountNo = "1"},
new Account {Id=2,AccountName = "B",AccountNo = "2"},
new Account {Id=3,AccountName = "C",AccountNo = "3"},
new Account {Id=4,AccountName = "D",AccountNo = "4"},
};
var userAccounts = (from u in users
from a in accunts
where u.Accounts.Contains(a.Id)
select new UserAccountViewModel
{
AccountNo = a.AccountNo,
AccountId = a.Id,
UserId = u.Id,
UserName = u.Name
}).ToList();

Inheritance one-to-one or zero relationship with IdentityUser class

This is my Classes :
public class ApplicationUsers : IdentityUser<Guid>
{
[InverseProperty("ApplicationUser")]
public virtual User User { get; set; }
[InverseProperty("ApplicationUser")]
public virtual WorkerTaxi WorkerTaxi { get; set; }
}
Person Abstract Class :
public abstract class Person
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid PersonId { get; set; }
....
}
User Class :
public class User:Person
{
....
[ForeignKey("PersonId")]
[InverseProperty("User")]
public virtual ApplicationUsers ApplicationUser { get; set; }
}
And WorkerTaxi Class :
public class WorkerTaxi : Person
{
.....
[ForeignKey("PersonId")]
[InverseProperty("WorkerTaxi")]
public virtual ApplicationUsers ApplicationUser { get; set; }
}
I want to have one to one or zero relationship between ApplicationUsers.cs and User.cs and ApplicationUsers.cs and WorkerTaxi.
When I run this test, it passes without any problem :
[TestMethod]
public async Task Verify_GetAllUser_Method()
{
using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var userManager =
serviceScope.ServiceProvider.GetRequiredService<UserManager<ApplicationUsers>>();
var id = Guid.NewGuid();
var applicationUsersService =
serviceScope.ServiceProvider.GetRequiredService<IApplicationUsersRepository>();
//--Arrange
var user = new ApplicationUsers
{
Id = id,
Email =
"X7n8N1JeD6CgecTnkdvmtH1sJZI=LY9R6hsG0Or3OSwv9nmDnQ==62ZLaR17B3BIAeQIIE57EZGlT8LwVlSEiB/ih3H1XxE=",
PhoneNumber = "qUqP5cyxm6YcTAhz05Hph5gvu9M=kByTpFP12CIONabzOFXQTg==fY6QbrosN2tQhXVZS82ZYg==",
UserName = "qUqP5cyxm6YcTAhz05Hph5gvu9M=FJZ1IeRcp4OHZzgdCwTJgQ==Jsgka2bvSbI/pzO3vbkOzg==",
User = new User
{
PersonId = id,
FirstName = "fTWDgtfCCdjrNPKhQpSYpA7NnV8=5dq1Tep3IfFLtabGzAKN0w==YA7slsTLCozHt7Mgn1pbXw==",
LastName = "qUqP5cyxm6YcTAhz05Hph5gvu9M=M6V+PMrCVyecbJPmE4853A==QWUrHVtr2I7ow6oDfZ/6ZQ==",
ProfileImage =
"SuGM+8hg8C3OCxpHd0Jgyu8/an4=yMfzTFs6xEp7SBxN8VvzcA==bJLLw5auqsptskn0hNXDd3nZPBIXbvaZEaQHq90IRCLkzCWflLCgi+NBBarLVdft",
Gender = Gender.Female
}
};
await userManager.CreateAsync(user: user);
const int expected = 1;
//--Actual
var actual = applicationUsersService.GetAllUser().Count;
//--Assert
Assert.AreEqual(expected: expected, actual: actual);
await userManager.DeleteAsync(user: user);
}
}
But when I run this test , I got this error :
[TestMethod]
public async Task Verify_GetAllWorker_Method()
{
using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var userManager =
serviceScope.ServiceProvider.GetRequiredService<UserManager<ApplicationUsers>>();
var applicationUsersService =
serviceScope.ServiceProvider.GetRequiredService<IApplicationUsersRepository>();
//--Arrange
var id = Guid.NewGuid();
var worker = new ApplicationUsers
{
Id = id,
Email =
"X7n8N1JeD6CgecTnkdvmtH1sJZI=uRzzvxiDclXJEkm+HwZcDw==WvfCqqAJRunNYo7DGno1ESxt00+ipRxYJ5jDJYq2rSg=",
PhoneNumber = "qUqP5cyxm6YcTAhz05Hph5gvu9M=alh9Stp8ok1XweUNhuDmIQ==NH5G7k2lHTOxDS24SGTXUg==",
UserName = "qUqP5cyxm6YcTAhz05Hph5gvu9M=bLI2HWYq0LnnkzLXKLvjwQ==V+9M/G5yeXMs7J71+vSbjQ==",
IsDeleted = false,
WorkerTaxi = new WorkerTaxi
{
PersonId = id,
BirthDate = DateTime.Now,
BirthPlace = "qUqP5cyxm6YcTAhz05Hph5gvu9M=T588vuuioT1zHIz7o4GTTw==A+K+k4/rXJmOhF2hbKX5DA==",
DeviceNumber = "pBHfwiS9RrhpHuwP7q4VYLgLUSw=cLNp+s2+DdCXbhux17lziw==I+BLV+WUWnsnLe7GEcKokw==",
DocumentImage =
"NbGHR/RrGeFenDWNnQ7LazgoKQY=9Wv1ku4hybd64s3ouNnhVg==gmUKXu+YU5hSkTfMJxOT4v7250tSLW0pc5SoY9JLVv/lQ1hnqQyyqZXAoG5MSCH8",
Driver = DriverType.Taxi,
DriverLicenseCode =
"u1YSKbB4W+eqDeKgx8rlrTPW1DQ=pIMDMdWdYtOye+d8GGf/lw==fyF11Wfkvpvm2uZJwCdwFg==",
DriverLicenseDate = DateTime.Now,
FatherName = "qUqP5cyxm6YcTAhz05Hph5gvu9M=0KIKpi8TXapLLANwAc3GWQ==Bqvh93OkxhhijBuLjiOwcg==",
FirstName = "fTWDgtfCCdjrNPKhQpSYpA7NnV8=G63WGL6+dmkDC0ExczxZrA==ggRbM3Df4dcmtYkp7KVohw==",
LastName = "qUqP5cyxm6YcTAhz05Hph5gvu9M=wTKx3cxcuxy5JqJd6obNSg==SxhwZxvzUlo7EprR+bHKTg==",
MeliCode = "IsVVIlMUNB9bd7y7yt4mlaFH76I=4+zDBqu22x2y9swI0EvgeA==S4u4K8ayuYp+hoRFV8s3yA==",
MeliCartImage =
"xbCEQTXyjhgIYCBWGOWDNX8yAeE=5eTXYSPcp2iDRLZSLGyBmw==R1ovaeqtJVbqgmqpCmF7wyrAFRgwybGpDnTLhejwRQ6rr0GvlLNzKvKoUSyJdafu",
ProfileImage =
"0EeKhLI1OG/f880FKzQG44gjiTo=TPMcxyaaPT4GcWC/k/laFw==vFV8ADkG8qoberKI+kEXPlIBslJUcUbAaVpWX4zf23gVooVw0jkhtFDJCxKQEIyr",
ShenasnamehCode = "gAc7m531mVKARZseE9/OL7eickU=57mUHw4a4NdLXLXdpPeEiA==txffKzYDvOh+bODJEN++0w==",
ShenasnamehImage =
"YRgn4oJ6HKekyKf06REGbtWpgiI=p6O8EVq53kzvAZEaIQYHGQ==Wu92TrssQkLe8AwcmUdyrvVeyV/b+Btf6PnYZsi4hUa9xknRBC0WVqnUE15VEHk3",
TaradodCode = "0kyb/jWQsXrWikM+TPHY/O/r62Q=iSMFo/gq5lvqo7L3SdhbFg==3v8kTvaulmjXfGJ6nwBTrQ==",
TaxiCode = "0kyb/jWQsXrWikM+TPHY/O/r62Q=bpyuyaihQRmk0orrStzgbw==7quLu5AYu9mJZkYujeHfMg==",
Gender = Gender.Female
}
};
await userManager.CreateAsync(user: worker);
const int expected = 1;
//--Actual
var actual = applicationUsersService.GetAllWorkerTaxi().Count;
//--Assert
Assert.AreEqual(expected: expected, actual: actual);
await userManager.DeleteAsync(user: worker);
}
}
PS.ServiceTest.ApplicationUsersRepositoryTest.Verify_GetAllWorker_Method
threw exception: System.InvalidCastException: Unable to cast object
of type 'PS.Model.Models.Taxi.WorkerTaxi' to type
'PS.Model.Models.User'.
The error was occurred when await userManager.CreateAsync(user: worker) was ran.
Where is the Problem ?
How can I fix that ?
Update
I found the real problem .... when it tries to add WorkerTaxi to Person table , the discriminator is always 0 base on this code :
public enum AccountType
{
User = 0,
TaxiWorker = 1
}
model.Entity<Person>()
.HasDiscriminator<int>(name: "Type")
.HasValue<WorkerTaxi>(value: Convert.ToInt32(value: AccountType.TaxiWorker))
.HasValue<User>(value: Convert.ToInt32(value: AccountType.User));
How can i fix that ?
should the code determine the discriminator or not ?

How to perform addition, update operation on extra table in identity server?

I have added the following UserLog table in the database using asnetidentity but i don't know how to perform add,update operation on the table.
public class User : IdentityUser
{
public virtual ICollection<UserLog> UserLogs { get; set; }
}
public class Context : IdentityDbContext<User, Role, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public Context(string connString)
: base(connString)
{
}
}
public class UserLog
{
[Key]
public Guid UserLogID { get; set; }
public string IPAD { get; set; }
public DateTime LoginDate { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser User { get; set; }
}
public System.Data.Entity.DbSet<UserLog> UserLog { get; set; }
Like in the following code, I am using inbuilt methods of these aspnetidentity tables but how to add an entry in the "UserLog" table when user login every time in the following method?
public override async Task AuthenticateLocalAsync(LocalAuthenticationContext ctx)
{
var username = ctx.UserName;
var password = ctx.Password;
var message = ctx.SignInMessage;
ctx.AuthenticateResult = null;
if (userManager.SupportsUserPassword)
{
var user = await FindUserAsync(username);
if (user != null)
{
if (userManager.SupportsUserLockout &&
await userManager.IsLockedOutAsync(user.Id))
{
return;
}
if (await userManager.CheckPasswordAsync(user, password))
{
if (userManager.SupportsUserLockout)
{
await userManager.ResetAccessFailedCountAsync(user.Id);
}
var result = await PostAuthenticateLocalAsync(user, message);
if (result == null)
{
var claims = await GetClaimsForAuthenticateResult(user);
result = new AuthenticateResult(user.Id.ToString(), await GetDisplayNameForAccountAsync(user.Id), claims);
}
ctx.AuthenticateResult = result;
}
else if (userManager.SupportsUserLockout)
{
await userManager.AccessFailedAsync(user.Id);
}
}
}
}
Step 1: Create an instance of UserLog
var userLog = new UserLog() { /* Set value for all the properties here */ };
Step 2: Add instance of UserLog to DbContext
context.Set<UserLog>().Add(userLog);
Step 3: Call DbContext.SaveChanges() to save changes to database.
context.SaveChanges();
Complete source code will looks like:
var userLog = new UserLog {
UserLogID = Guid.NewGuid(),
IPAD = "Some Value",
LoginDate = DateTime.Now,
UserId = "User Id Here"
};
var context = new Context();
context.Set<UserLogs>().Add(userLog);
context.SaveChanges();

Categories

Resources