Many to Many relationship not updating properly in Sqlite-Extensions - sqlite-net-extensions

I'm creating a chat app in Android using sqlite-extensions for creating relationships between database tables.
Here's the wall of code to set the stage:
Conversation Table
public class Conversation
{
[PrimaryKey, Unique, AutoIncrement]
public int ID { get; set; }
public string toUser { get; set; }
public string FromUser { get; set; }
[ManyToMany(typeof(ChatConversation), CascadeOperations = CascadeOperation.All)]
public List<ChatMessage> ChatMessages { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<ConversationDeleted> DeletedConversations { get; set; }
public DateTime CreatedAt { get; set; }
public int UserID { get; set; }
}
ChatMessage Table
public class ChatMessage
{
[PrimaryKey, Unique, AutoIncrement]
public int ChatMessageID { get; set; }
public string fromUser { get; set; }
public string toUser { get; set; }
public string Message { get; set; }
public bool DeliveryStatus { get; set; }
public DateTime CreatedAt { get; set; }
[Unique, AutoIncrement, NotNull]
public Guid UniqueID { get; set; }
[ManyToMany(typeof(ChatConversation), CascadeOperations = CascadeOperation.All)]
public List<Conversation> Conversation { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<MessageDeleted> MessagesDeleted { get; set; }
public int UserId { get; set; }
}
Intermediated table for chat and conversation table relationship
class ChatConversation
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[ForeignKey(typeof(Conversation))]
public int ConversationID { get; set; }
[ForeignKey(typeof(ChatMessage))]
public int ChatID { get; set; }
}
When I message a user, it creates a new conversation cascaded with a chat message object. When I login to the other user's account on the same device, a new conversation is created and that same chat message is cascaded with it. Now the issue is, after the user receives the message, the relationship that was formed between the conversation created by the first user and the chat message is no longer there.
Here the user sends a message:
string messageText = messageET.Text.ToString();
if (TextUtils.IsEmpty(messageText))
{
return;
}
var member = dbr.GetMember(loggeduser);
var uniqueID = Guid.NewGuid();
proxySubscriber.InvokeSendMessage(user, messageText, uniqueID);
ChatMessage chatMessage = new ChatMessage();
chatMessage.Message = messageText;
chatMessage.fromUser = loggeduser;
chatMessage.toUser = user;
chatMessage.CreatedAt = DateTime.Now;
chatMessage.UniqueID = uniqueID;
chatMessage.UserId = member.UserID;
dbr.insertUpdateChat(chatMessage);
var duplicat_chat_msg = dbr.GetChat().ToList().Where(u => u.UniqueID == uniqueID).FirstOrDefault();
List<ChatMessage> msg_list = new List<ChatMessage>();
msg_list.Add(duplicat_chat_msg);
var update_conv = dbr.GetConversations().Where(f => f.FromUser == loggeduser && f.toUser == user && f.UserID == member.UserID).FirstOrDefault();
var deleted_conversation = dbr.GetConversationDeleted().Where(d => d.UserID == member.UserID && d.ConversationID == update_conv.ID).FirstOrDefault();
bool HasConversation = false;
if (deleted_conversation != null)
{
HasConversation = true;
}
if (update_conv != null && !HasConversation)
{
update_conv.ChatMessages.Add(duplicat_chat_msg);
dbr.UpdateConversationData(update_conv);
}
else
{
Conversation conv = new Conversation { ChatMessages = msg_list, CreatedAt = DateTime.Now, FromUser = loggeduser, toUser = user, UserID = member.UserID };
dbr.insertConversationData(conv);
}
displayMessage(chatMessage);
messageET.Text = "";
};
Here the user receives the chat:
var member = dbr.GetMember(loggedonuser);
chat_msg.DeliveryStatus = true;
var chat = dbr.GetChat().ToList().Where(c => c.UniqueID == chat_msg.UniqueID).FirstOrDefault();
if (chat == null)
{
dbr.insertUpdateChat(chat_msg);
}
var conversation = dbr.GetConversations().Where(f => f.toUser == chat_msg.toUser && f.FromUser == chat.fromUser && f.UserID == member.UserID).FirstOrDefault();
var deleted_conversation = dbr.GetConversationDeleted().Where(d => d.UserID == member.UserID && d.ConversationID == conversation.ID).FirstOrDefault();
bool HasConversation = false;
if (deleted_conversation != null)
{
HasConversation = true;
}
if (conversation == null && !HasConversation)
{
Conversation conv = new Conversation { toUser = chat_msg.toUser, FromUser = chat_msg.fromUser, CreatedAt = DateTime.UtcNow, UserID = member.UserID };
conv.ChatMessages = new List<ChatMessage> { chat_msg };
dbr.insertConversationData(conv);
}
else
{
conversation.ChatMessages.Add(chat_msg);
dbr.UpdateConversationData(conversation);
}
When the sender creates a conversation, a relationship is formed between conversation and chat message sent but when the second user receives the message, this relationship is broken and a new relationship is formed between the conversation created by the receiver of the message. With the relationships being many to many, this shouldn't be happening. Here's where the conversation is inserted into the db:
public string insertConversationData(Conversation data)
{
try
{
{
db.InsertOrReplaceWithChildren(data, recursive:true);
}
return "Single data file inserted or updated";
}
catch (SQLiteException ex)
{
Thread.Sleep(500);
return insertConversationData(data);
}
}
Can someone please look at my code and tell me what is wrong?

You have to take into account that calling UpdateWithChildren will save the current status of the object.
So what you're doing right now is equivalent to:
update_conv.ChatMessages.RemoveAll();
update_conv.ChatMessages.Add(message);
Because ChatMessages is empty by the time you add a new message.
On updating, SQLite-Net Extensions will remove all relationships but that one because that what the new list looks like.
Add this line:
update_conv.GetChildren();
After this line:
var update_conv = dbr.GetConversations().Where(f => f.FromUser == loggeduser && f.toUser == user && f.UserID == member.UserID).FirstOrDefault();
The same happens with your second piece of code.

Related

NullReferenceException When Using a ViewModel

I'm trying to use a ViewModel in my ASP.NET MVC project, but whenever I use it, I get a null reference error for some reason. The latest situation where I have this problem, is here (it works fine when I use the Model itself, but when I use the ViewModel, I get that error):
My Model:
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string MaritalStatuse { get; set; }
public DateTime DateOfBirth { get; set; }
My ViewModel:
public class PersonFormViewModel
{
public Person Person { get; set; }
public string Title
{
get
{
if (Person != null && Person.Id != 0)
return "Edit";
return "New";
}
}
}
My Controler:
public ActionResult Edit(int id)
{
var person = _context.Persons.FirstOrDefault(p => p.Id == id);
var viewModel = new PersonFormViewModel
{
Person =
{
FirstName = person.FirstName,
LastName = person.LastName,
Gender = person.Gender,
DateOfBirth = person.DateOfBirth,
MaritalStatuse = person.MaritalStatuse
}
};
return View(viewModel);
}
When running this code, the "viewModel" in the Controller is null, even though the "person" is not. Can anyone please help me with this? (I am new to ASP.NET, as you might've guessed).
I think Person needs to be instantiated before assigning values to it.
public ActionResult Edit(int id)
{
var person = _context.Persons.FirstOrDefault(p => p.Id == id);
var viewModel = new PersonFormViewModel
{
Person = new Person
{
FirstName = person.FirstName,
LastName = person.LastName,
Gender = person.Gender,
DateOfBirth = person.DateOfBirth,
MaritalStatuse = person.MaritalStatuse
}
};
return View(viewModel);
}

How to display the details of a row from multiple tables using LINQ?

I'm stuck here on a situation wherein I should display the details of a person together with the list of his/her allocations. I've done creating a view model to pass the data to a view but the result is:
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[EKCMIDTA.ViewModels.EmployeeDetailsVM]', but this dictionary requires a model item of type 'EKCMIDTA.ViewModels.EmployeeDetailsVM'.
For the TicketScannings table, I just wanted to know whether the person has used some of the allocations, and to count how many were used, regardless it it's null.
I hope someone can help me with this.
Thanks!
Controller:
public ActionResult GetDetails(int empId)
{
var employeeInformation = identityContext.AspNetUsers.Find(empId);
var employeeDetails = dbContext.TicketAllocations.Include(a => a.AllocationCategory).Where(t => t.CMId == empId).ToList();
var query = (from alloc in dbContext.TicketAllocations
join scan in dbContext.TicketScannings
on alloc.Id equals scan.TicketAllocationId
join card in dbContext.CardNumberAssignments
on alloc.CMId equals card.CMId into a
from card in a.DefaultIfEmpty()
join reserve in dbContext.ReservedCardNumbers
on card.CardNumberId equals reserve.Id into b
from reserve in b.DefaultIfEmpty()
where (alloc.CMId == empId)
select new EmployeeDetailsVM()
{
Employee = new Employee()
{
FirstName = employeeInformation.FirstName,
LastName = employeeInformation.LastName,
CMId = employeeInformation.Id,
CardNumber = reserve == null ? "No Card Number yet" : reserve.CardNumber,
QRCode = card == null ? "No QR Code yet" : card.QRCode
},
GetTicketAllocations = employeeDetails
});
return View(query);
View Model:
public class EmployeeDetailsVM
{
public Employee Employee { get; set; }
public IEnumerable<Allocation> GetTicketAllocations { get; set; }
}
public class Employee
{
public string CMId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CardNumber { get; set; }
public string QRCode { get; set; }
}
public class Allocation
{
public int AllocationId { get; set; }
public string AllocationName { get; set; }
public int Quantity { get; set; }
public bool IsActive { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedDate { get; set; }
public string ModifiedBy { get; set; }
public DateTime ModifiedDate { get; set; }
}
View:
#model EKCMIDTA.ViewModels.EmployeeDetailsVM
Looks like your view is only accepting a model of a single EmployeeDetailsVM, but you're passing in a query which would could return multiple.
so you can change #model EKCMIDTA.ViewModels.EmployeeDetailsVM to #model IEnumerable<EKCMIDTA.ViewModels.EmployeeDetailsVM>
or change your GetDetails action to return View(query.FirstOrDefault());
Edit based on comment
public ActionResult GetDetails(int empId)
{
var employeeInformation = identityContext.AspNetUsers.Find(empId);
var employeeTickets = dbContext.TicketAllocations.Include(a => a.AllocationCategory).Where(t => t.CMId == empId).ToList();
var employeeDetails = (from alloc in dbContext.TicketAllocations
join scan in dbContext.TicketScannings
on alloc.Id equals scan.TicketAllocationId
join card in dbContext.CardNumberAssignments
on alloc.CMId equals card.CMId into a
from card in a.DefaultIfEmpty()
join reserve in dbContext.ReservedCardNumbers
on card.CardNumberId equals reserve.Id into b
from reserve in b.DefaultIfEmpty()
where (alloc.CMId == empId)
select new EmployeeDetailsVM()
{
Employee = new Employee()
{
FirstName = employeeInformation.FirstName,
LastName = employeeInformation.LastName,
CMId = employeeInformation.Id,
CardNumber = reserve == null ? "No Card Number yet" : reserve.CardNumber,
QRCode = card == null ? "No QR Code yet" : card.QRCode
}
}).FirstOrDefault();
if (employeeDetails != null)
employeeDetails.GetTicketAllocations = employeeTickets;
return View(employeeDetails);
}

The model item passed into the dictionary is of type 'System.Security.Principal.WindowsPrincipal', but requires a model 'Namepace.Models.User'

public ActionResult UserProfileSetting(int id)
{
int UserID = Convert.ToInt32(Session["UserID"]);
if (UserID > 0)
{
var User = DB.Users.Where(x => x.UserID == id).FirstOrDefault();
}
else
{
return PartialView("_Error404");
}
return View(User);
}
public partial class User
{
public int UserID { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Password { get; set; }
public Nullable<bool> IsEmailVerified { get; set; }
public Nullable<System.Guid> ActivationCode { get; set; }
public string ResetPasswordCode { get; set; }
public string FullName { get; set; }
public string UserName { get; set; }
}
<li class="dropdown" style="margin-top:2px">
<i class="fa fa-cog"></i> Setting</li>
</li>
Getting first time this error and getting error when checking the session value null or not . Without IF statement it is running right. Where i am doing mistake.
The problem in your code is the scoping of your "User" variable in your if statement.
public ActionResult UserProfileSetting(int id)
{
int UserID = Convert.ToInt32(Session["UserID"]);
if (UserID > 0)
{
var User = DB.Users.Where(x => x.UserID == id).FirstOrDefault();
//this is not the same object that are returning below. This is an instance of your User class. It is only available in this if statement.
}
else
{
return PartialView("_Error404");
}
return View(User); //This is not the same object as the "User" variable above. This is a System.Security.Principal.WindowsPrincipal
}
Try changing your code to this and it should fix that error:
public ActionResult UserProfileSetting(int id)
{
int UserID = Convert.ToInt32(Session["UserID"]);
User user = null;
if (UserID > 0)
{
user = DB.Users.Where(x => x.UserID == id).FirstOrDefault();
}
else
{
return PartialView("_Error404");
}
return View(user);
}

How to join two tables in linq and not get anonymous type result set

I would like to get a result set from both PriceAssocationLookup and PriceAssociation tables. How do I do this and not get an error due to Anonymous Type?
Here is my code:
IEnumerable<IPriceAssociationLookupRepository> IPriceAssociationLookupRepository.GetPacs(string upc)
{
using (PortalDataEntities entities = new PortalDataEntities())
{
var priceAssociationLookups = (from priceassociationlookup in entities.PriceAssociationLookups
join priceassociation in entities.PriceAssociations on priceassociationlookup.PriceAssociationCode equals priceassociation.PriceAssociationCode
where priceassociationlookup.Upc == upc
select priceassociationlookup ).ToList();
return priceAssociationLookups;
}
}
Create a ViewModel and add properties for the columns you want to return and return List of the view model type, here is my code, the way i used to do :
List<PreviousTest> Result = (from d in db.dc_tpatient_bookingd
join og in db.dc_tp_organization
on d.clientid equals og.OrgId into a
from og in a.DefaultIfEmpty()
from t in db.dc_tp_test
from p in db.dc_tp_tprocess
where d.bookingid == BookingID
&& t.TestId == d.testid
&& d.ProcessID == p.processid
&& d.bookingdid != BookingDID
select new PreviousTest
{
BookingID = d.bookingid,
BookingDId = d.bookingdid,
TestID = t.TestId,
TestName = t.Test_Name,
ProcessName = p.name,
ProcessID = p.processid,
ClientID = d.clientid
}).ToList();
Here is my viewmodel :
public class PreviousTest
{
public long BookingID { get; set; }
public long BookingDId { get; set; }
public long TestID { get; set; }
public string TestName { get; set; }
public long ProcessID { get; set; }
public string ProcessName { get; set; }
public string ClientID { get; set; }
}

Exception NullReferenceException in controller (asp.net mvc)

There are Supplier model and User model in my project, every Supplier has a few Users
Supplier model
public class SupplierRow
{
public Guid Id { get; set; }
public string FullName { get; set; }
public bool Subscribed { get; set; }
public bool Active { get; set; }
public int Visits { get; set; }
public List<UserRow> Users { get; set; }
public bool AllInactive
{
get
{
foreach (UserRow ur in Users)
{
if (ur.Status == 1) return false;
}
return true;
}
}
}
and User model
public class UserRow
{
public Guid Id { get; set; }
public string FullName { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Status { get; set; }
public int Role { get; set; }
public Guid SupplierId { get; set; }
}
then I use my models in controller
public ActionResult Grid(bool? active)
{
var suppliers = Context.Suppliers.AsNoTracking()
.WhereIf(active != null, e => e.Active == active)
.Select(e => new SupplierRow
{
Id = e.Id,
FullName = e.FullName,
Active = e.Active,
Visits = e.Visits,
})
.ToList();
List<Guid> supplierIds = new List<Guid>();
foreach (SupplierRow sr in suppliers)
{
supplierIds.Add(sr.Id);
}
var users = Context.Users.AsNoTracking()
.Where(e => supplierIds.Contains(e.SupplierId.Value))
.Select(e => new UserRow
{
Id = e.Id,
FullName = e.FullName,
Email = e.Email,
Name = e.Name,
Status = e.Status,
Role = e.Role,
SupplierId = e.SupplierId.Value
}).ToList();
foreach (UserRow ur in users)
{
foreach (SupplierRow sr in suppliers)
{
if (ur.SupplierId == sr.Id)
{
sr.Users.Add(ur);
}
}
}
return PartialView("_Grid", suppliers);
}
but when I try to debug my project I get some exception here
What's wrong? How can I fix that?
Your Users list are not initialized. Create a new list before accessing it Users = new List<UserRow>(); You can change the SupplierRow class:
public class SupplierRow {
private List<UserRow> users = new List<UserRow>();
public List<UserRow> Users
{
get { return users; }
set { users = value; }
}
...
}
or in the constructor:
public class SupplierRow
{
public SupplierRow()
{
Users = new List<UserRow>();
}
public List<UserRow> Users { get; set; }
...
}
or before accessing it:
foreach (UserRow ur in users)
{
foreach (SupplierRow sr in suppliers)
{
sr.Users = new List<UserRow>();
if (ur.SupplierId == sr.Id)
{
sr.Users.Add(ur);
}
}
}
or you can just use linq:
foreach (SupplierRow sr in suppliers)
{
sr.Users = users.Where(user => user.SupplierId == sr.Id);
}
return PartialView("_Grid", suppliers);

Resources