Entity Framework NullReferenceException when accessing lists - c#

I am using Windows Forms and I'm just learning Entity Framework and I have a question: I created a Customer class with a list type of Item and with Entity Framework I created a table for it. But when I try to get the list from it, I get a NullReference exception.
This are my classes:
public class Item
{
public int Id{ get; set; }
public string ItemName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string UserName { get; set; }
public string PassWord { get; set; }
public List<Item> Items { get; set; }
}
And this is the method I created to get the customer list based on the ID of the customer. I get the Id from the login and it works just fine:
public List<Item> CustomerItems()
{
using var context = new CustomerContext();
var customer= context.Customers.Find(Id);
var items = customer.Items;
return items;
}
I wanted to use this method to update the datagrid and add new items to the list.
I added some entries to the Item table but they don't show up in the datagrid.

Please check the loading related data section of the docs for options how to load linked data.
Also in this case you can just use proper query returning only needed data. For example:
var items = context.Customers
.Where(c => c.Id == Id)
.SelectMany(c => c.Items)
.ToList();

In the code snippet presented above a NullReferenceExceptionmay occur in the access to Items in the expression customer.Items when customer is null. The code should be modified to handle the null value, e.g.
var items = (customer == null) ? new List<Item>() : customer.Items;

Related

Using .Select to get single item from sub list, then assigning individual properties of that item to view model

I have pretty much solved this problem but I am wondering whether there is a more efficient way of doing this using Entity framework / SQL.
Essentially, what i am doing is performing a subquery to get a SINGLE item on a list of objects that are connected to a parent entity. I then want to extract only a few columns from that single entity.
The first way, which doesn't work but shows my possible thought process was to put each object into a temporary variable and then create the view:
_context.IcoInfos.Select((i) =>
{
var reward = i.SocialRewards.OrderByDescending(s => s.EndDate).FirstOrDefault();
return new IcoInfoRewardCountViewModel()
{
CampaignName = i.Name,
CurParticipants = reward.CurParticipants,
Title = reward.CustomTitle,
IsLive = reward.IsLive
};
});
The second way, which works, I am creating a temporary model which stores the single database row of the sublist result...
_context.IcoInfos.Select((i) => new
{
Reward = i.SocialRewards.OrderByDescending(s => s.EndDate).FirstOrDefault(),
IcoName = i.Name
}).Select(t => new IcoInfoRewardCountViewModel()
{
CampaignName = t.IcoName,
CurParticipants = t.Reward.CurParticipants,
Title = t.Reward.CustomTitle,
IsLive = t.Reward.IsLive
}).ToList();
My question is, is this second way the only/best way to achieve this?
Your second approach is ok but for bigger application will cause you trouble if application growth larger and you have a lot information to store in the model.
So I think you can use automapper to make your code more clean.
Example
To use autoampper I need to define a model class and DTO class that share some same properties.
public class Comment
{
public string Content { get; set; }
public virtual Comment ParentComment { get; set; }
public virtual Post Post { get; set; }
public virtual User? User { get; set; }
public CommentStatus CommentStatus { get; set; }
}
public class CommentDto
{
public int Id { get; set; }
public Guid UniqeId { get; set; }
public string Content { get; set; }
public Comment ParentComment { get; set; }
public CommentStatus CommentStatus { get; set; }
public DateTime DateCreated { get; set; }
}
I also need to define the profile class to register mapping
public class CommentProfile : Profile
{
public CommentProfile()
{
CreateMap<Comment, CommentDto>(MemberList.None).ReverseMap();
}
}
Then I will need to register into DI container in startup.cs
services.AddAutoMapper();
Then I can use like this
var comments = await _unitOfWork.Repository<Comment>().Query()
.Include(x => x.User)
.Include(c => c.Post)
.Select(x => new CommentViewModel
{
Comment = _mapper.Map<Comment, CommentDto>(x),
})
.ToListAsync();
It will make the code more clear and I dont have to do manual mapping

Access count of related records of an entity Entity Framework

I have two models:
public class HouseType
{
public int Id { get; set; }
public string TypeName { get; set; }
public virtual IEnumerable<HouseModel> HouseModels { get; set; }
}
and
public class HouseModel
{
public int Id { get; set; }
public string ModelName { get; set; }
[DisplayFormat(DataFormatString = "{0:n2}")]
public double StandardPrice { get; set; }
[ForeignKey("HouseType")]
public int HouseTypeID { get; set; }
public virtual HouseType HouseType { get; set; }
public virtual IEnumerable<HouseUnit> HouseUnits { get; set; }
}
I am returning a JSON result, so as expected I cannot manipulate it in a view, because the display is handled by a javascript file that I made.
I am trying to retrieve the number of HouseModel that is contained by HouseType. I have tried:
db.HouseTypes.Select(h => new
{
HouseCount = h.HouseModels.Count()
}).ToList();
But Entity Framework complains about it. How can I access the count of related records inside an entity? Any help will be much appreciated. Thanks.
Use
public virtual ICollection<HouseUnit> HouseUnits { get; set; }
instead of
public virtual IEnumerable<HouseUnit> HouseUnits { get; set; }
Hope this helps.
Simply speaking, the trouble is that EF is trying to execute the .Select() statement on the db server but, of course, the db server does not know how to create a new object.
You first need to bring back the counts then create your objects so something like this should work better:
var listOfCounts = db.HouseTypes
.Select(h => h.HouseModels.Count())
.ToList()
.Select(c => new
{
HouseCount = c
})
.ToList();
in this example when the first .ToList() is executed the db needs only return a set of numbers (the counts of HouseModels in each HouseType) then we have a List<int> in local memory from which we can create our objects with the second Select statement.
As an aside...
It wasn't part of your original question but maybe you'd want to consider a dictionary rather than a list so you have some means of identifying which count of HouseModels belonged to each HouseType? in which case we could do something like:
Dictionary<int,string> houseModelCounts = db.HouseTypes
.ToDictionary(h => h.Id, h => h.HouseModels.Count());
which would give a dictionary keyed with the HouseType Id with values for the count of HouseModels in each type. I don't know your context though so maybe unnecessary for you?

Entity Framework inserting new rows instead of updating them

I have a problem when I am updating data to database. When I want to update data, Entitiy Framework adds new rows to tables that can have multiple rows (tables that have foreign key).
Database model:
When I update Phone/Contact or Tags entity, Entity Framework automatically adds new row instead of updating it
Here is code that I used:
public string UpdateContact(Contact contact)
{
if (contact != null)
{
int id = Convert.ToInt32(contact.id);
Contact Updatecontact = db.Contacts.Where(a => a.id == id).FirstOrDefault();
Updatecontact.firstname = contact.firstname;
Updatecontact.lastname = contact.lastname;
Updatecontact.address = contact.address;
Updatecontact.bookmarked = contact.bookmarked;
Updatecontact.city = contact.city;
Updatecontact.notes = contact.notes;
Updatecontact.Emails1 = contact.Emails1;
Updatecontact.Phones1 = contact.Phones1;
Updatecontact.Tags1 = contact.Tags1;
db.SaveChanges();
return "Contact Updated";
}
else
{
return "Invalid Record";
}
}
EDIT:
Here is EF Model code:
Contact:
public partial class Contact
{
public Contact()
{
this.Emails1 = new HashSet<Email>();
this.Phones1 = new HashSet<Phone>();
this.Tags1 = new HashSet<Tag>();
}
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string address { get; set; }
public string city { get; set; }
public Nullable<byte> bookmarked { get; set; }
public string notes { get; set; }
public virtual ICollection<Email> Emails1 { get; set; }
public virtual ICollection<Phone> Phones1 { get; set; }
public virtual ICollection<Tag> Tags1 { get; set; }
}
Emails/Tags and Phone have same model (with different name for value)
public partial class Email
{
public int id { get; set; }
public int id_contact { get; set; }
public string email1 { get; set; }
public virtual Contact Contact1 { get; set; }
}
Update properties rather than set new objects.
Updatecontact.Emails1.email1 = contact.Emails1.email1;
Updatecontact.Phones1.number = contact.Phones1.number;
Updatecontact.Tags1.tag1 = contact.Tags1.tag1;
Edit: seems that your contact model has lists of emails, phones and tags. If this is so, then simple assignment won't work. Instead, when sent from the client, you have to find one-by-one and update:
foreach ( var email in contact.Emails1 )
{
// first make sure the object is retrieved from the database
var updateemail = Updatecontact.Emails1.FirstOrDefault( e => e.id == email.id );
// then update its properties
updateemail.email1 = email.email1;
}
// do the same for phones and tags
It's doing that because you're setting the different HashSet values to the values of a completely different collection, namely from what you call contact in that method. In order for you to properly do an update, you're going to have to loop through the emails, phones, and tags to check if those need to be added/updated/deleted on the actual object that you're trying to update.
First, why do you have to search for the contact if you are already receiving it by parameter? That makes me think that you are creating a new one because you are in a different context, if so, then it creates a new record because you have 2 different object in 2 different context.
Try using just one object in the same context to update, EF should mark the object to modification by itself, if not then try making sure before saving that your object has EntityState.Modified.

How to query a flatten sub collection in RavenDb? Index needed?

I am using RavenDb in C# web project. I have an object that I need to query its child collection with 1 row per child object and some of the root/parent object properties.
Note: This is not the actual design, just simplified for this question.
public class OrderLine
{
public string ProductName { get; set; }
public int Quantity { get; set; }
public DateTime? ShipDate { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
public DateTime OrderDate { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
The order with the orderlines is one single document. ShipDate will be updated on each line because not all products are always in stock.
I need to be able to create a list of the last 10 products sent with the following columns:
OrderId
Customer
ProductName
ShipDate
This doesn't work because SelectMany is not supported:
var query = from helper in RavenSession.Query<Order>()
.SelectMany(l => l.OrderLines, (order, orderline) =>
new { order, orderline })
select new
{
helper.order.OrderId,
helper.order.CustomerName,
helper.orderline.ProductName,
helper.orderline.ShipDate
};
var result = query.Where(x => x.ShipDate.HasValue)
.OrderByDescending(x => x.ShipDate.Value).Take(10);
I believe the right thing to do isto create an Index that will flatten out the list but I haven't had any success. I don't believe a Map-Reduce situation will work because as I understand it will effectively does a group by which Reduces the number of documents to less rows (in the index). But in this case, I am trying to expand the number of documents to more rows (in the index).
I would rather not put each OrderLine in a separate document but I do not know what my options are.
Since you want to filter and sort by fields in the subclass, you'll need to make sure all the fields you want are indexed and stored.
public class ShippedItemsIndex
: AbstractIndexCreationTask<Order, ShippedItemsIndex.Result>
{
public class Result
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public DateTime ShipDate { get; set; }
}
public ShippedItemsIndex()
{
Map = orders =>
from order in orders
from line in order.OrderLines
where line.ShipDate != null
select new
{
order.OrderId,
order.CustomerName,
line.ProductName,
line.Quantity,
line.ShipDate
};
StoreAllFields(FieldStorage.Yes);
}
}
Then you can project from the index into your results.
var query = session.Query<Order, ShippedItemsIndex>()
.ProjectFromIndexFieldsInto<ShippedItemsIndex.Result>()
.OrderByDescending(x => x.ShipDate)
.Take(10);
var results = query.ToList();
Here is a complete test demonstrating.

LINQ to Entities query error

I am encountered an error that I am not familier with. I tried to google with no success.
I wrote the following query where I am having this error.
The entity or complex type 'MyWebProject.Models.UserDetail' cannot be constructed in a LINQ to Entities query.
The query:
UsersContext db = new UsersContext();
var userdata = (from k in db.UserDetails
where k.UserId == WebSecurity.CurrentUserId
select new UserDetail()
{
FullName = k.FullName,
Email = k.Email,
About = k.About,
Link = k.Link,
UserSchool = new School()
{
SchoolId = k.UserSchool.SchoolId,
SchoolName = k.UserSchool.SchoolName
},
UserCourse = new Course()
{
CourseId=k.UserCourse.CourseId,
CourseName=k.UserCourse.CourseName
},
Country=k.Country
}).FirstOrDefault();
Class:
public class UserDetail
{
public int Id { get; set; }
public int UserId { get; set; }
public string FullName { get; set; }
public string Link { get; set; }
public bool? Verified { get; set; }
public string Email { get; set; }
public string About { get; set; }
public School UserSchool { get; set; }
public Course UserCourse { get; set; }
public string Country { get; set; }
}
public class School
{
public int SchoolId { get; set; }
public string SchoolName { get; set; }
public string Country { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public School School { get; set; }
}
Any idea what went wrong??
It looks like it is due to how you are creating the complex properties School and Course in the middle of the query. It would be better to select the User (remove the select transformation), then use navigation properties to access those objects instead of building them manually. The navigation are meant for this as long as you have the proper relations built with foreign keys.
UsersContext db = new UsersContext();
var userdata = (from k in db.UserDetails
where k.UserId == WebSecurity.CurrentUserId})
.FirstOrDefault();
// access navigation properties which will perform the joins on your behalf
// this also provides for lazy loading which would make it more effecient. (it wont load the school object until you need to access it)
userdata.School
userdata.Course
MSDN article about navigation properties: http://msdn.microsoft.com/en-us/library/vstudio/bb738520(v=vs.100).aspx
This should give you what you want. It will load your objects as part of the query (and not rely on lazy loading).
UsersContext db = new UsersContext();
var userdata = db.UserDetails.Include(x => x.UserSchool)
.Include(x => x.UserCourse)
.Include(x => x.Country)
.Where(x => x.UserId == WebSecurity.CurrentUserId)
.FirstOrDefault();
I think it's because your entity has the same name of the object you're trying to create. Try renaming the object you want to return back. If you want to return the same type as your entity try the eager loading with .Include("relationshipname") feature.
A great answer from #Yakimych is given below.
You cannot (and should not be able to) project onto a mapped entity. You can, however, project onto an annonymous type or onto a DTO:
public class ProductDTO
{
public string Name { get; set; }
// Other field you may need from the Product entity
}
And your method will return a List of DTO's.
public List<ProductDTO> GetProducts(int categoryID)
{
return (from p in db.Products
where p.CategoryID == categoryID
select new ProductDTO { Name = p.Name }).ToList();
}
Mapped entities in EF basically represent database tables. If you project onto a mapped entity, what you basically do is partially load an entity, which is not a valid state. EF won't have any clue how to e.g. handle an update of such an entity in the future (the default behaviour would be probably overwriting the non-loaded fields with nulls or whatever you'll have in your object). This would be a dangerous operation, since you would risk losing some of your data in the DB, therefore it is not allowed to partially load entities (or project onto mapped entities) in EF.
For more details please go to the following link:
The entity cannot be constructed in a LINQ to Entities query

Categories

Resources