Ef6: count of the references - c#

I have 2 simple objects.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public List<Message> Messages { get; set; }
//public int MessageCount { get; set; }
}
Public class Message
{
public int Id { get; set; }
public string Text { get; set; }
}
I need to show MessageCounts for each person which is simply the number of messages a person has wrote. How can I do it?
I have some ideas but I think they will be very slow, since I will need to return a list of persons and on each I need the count.
My Ideas
1- in code
[NotMapped]
public int MessageCount { get {return Messages.Count()}; private set; }
This is the simplest way that I could imagine, but at the same time it seems to be very slow on a large database since for each person it needs to go and fetch the message count separately which is crazy.
2- computed column with a function to return it
It seems like a better plan. right?
However, I could not find the whole solution anywhere.
I know I can decorate my property with [DatabaseGenerated(DatabaseGeneratedOption.Computed)] which will make it read from a computed field, but then how to create a function that returns the value and use that?
I've found something here but he uses the code from the same table which can be done with normal computed fields.
I also this post Calculated column in EF Code First but non of the answers was to my question.
--
Considering my question, it should be something that you can seen in many applications. Isn't there any easy and high performance way to do it?
Update
Thanks to people who commented, I guess the best way is to create 2 types, 1 that corresponds to the real person class and using that for normal CRUD actions and the other which is just a view coming from a join to show lists.
Any ideas? :)

You will need a relation field between the Message and the user who wrote it, something like this:
Public class Message
{
public int Id { get; set; }
public string Text { get; set; }
public int UserId {get; set; }
}
then when you can count the messages with a simple linq query like this:
context.Messages.Count(m=> m.UserId == id);
linq is optimized to do this the best as possible, but is the database is very large you will need an approach of the optimization by design, and is better to have a persisted field of the messages count and you can increase it with triggers in the publication or something like that.

Related

How to identify a dependent entity in one to one relationship Entity Framework Core?

I have studied many articles about how to config one to one relationships. I have learned it.
But I couldn't understand how to find a dependent entity in one to one relationship?
For instance, we have two entities User and Developer. How to understand which of them is a dependent entity? Because I think we must add a foreign key to a dependent entity. So the first things to do, we need to find a dependent entity.
public class User
{
public int Id {get;set;}
public string PasswordHash { get; set; }
public string FullName { get { return FirstName + " " +LastName;}}
public GenderType Gender { get; set; }
public bool IsActive { get; set; }
public DateTimeOffset LastLoginDate { get; set; }
public string FirstName { get ; set ; }
public string LastName { get; set; }
}
public class Developer
{
public int Id { get; set; }
public byte Image { get; set; }
public int Age { get; set; }
public string Resume { get; set; }
}
A dependent is something that depends on something else. A baby is dependent on its mother for food etc. Identify which entity can stand alone, without the other.
For example, you may decide that a User might not be a Developer but a Developer is always a User - in this case your relationship is actually 1:0..1 (user:developer) and you're looking at Developer being a subclass of a User. If. You could alternatively arrange things in a has-a fashion, and Developer has a User property (but User doesn't have a Developer property because not every User is a developer)
You may decide that you can never have one without the other- in which case they would probably be a good candidate for being in the same table/same client side entity
To some extent the question can be academic; there may be situations where you want to treat one as dependent, and others where it's the inverse way round. It'll probably help you overall though if you make a decision about how your entities are related on the client side and this will drive how you map them on the database side

Navigation property doesn't get loaded - EF

I'm having 3 models Items, Branches and ItemsInBranches defined as follows
public class Items
{
public int Id { get; set; }
public string Barcode { get; set; }
public string Name { get; set; }
public int SizeId { get; set; }
public int Price { get; set; }
public int DiscountId { get; set; }
public int ShortageMargin { get; set; }
[NotMapped]
public double ActualPrice
{
get
{
double amount = ((double)Price * (double)Discount.Amount / 100);
double price = (Price - amount < 0) ? 0 : Price - amount;
return price;
}
}
public Discounts Discount { get; set; }
public ItemSizes Size { get; set; }
public ICollection<ItemsInBranches> ItemsInBrach { get; set; }
}
public class Branches
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public ICollection<Employees> Employees { get; set; }
public ICollection<TransactionLog> TransacionLogs { get; set; }
public ICollection<ItemsInBranches> ItemsInBranch { get; set; }
}
public class ItemsInBranches
{
public int Id { get; set; }
public int ItemId { get; set; }
public int BranchId { get; set; }
public int Amount { get; set; }
[NotMapped]
public bool IsShort
{
get
{
return Amount < Item.ShortageMargin;
}
}
public Items Item { get; set; }
public Branches Branch { get; set; }
}
whenever I try to load items in branches using the following code I Branch navigation property gets loaded just fine but I the Items is always set to null
public IEnumerable<StorageViewModel> GetStorage(int? BranchId)
{
var storage = Find(x => true).Select(s => new StorageViewModel
{
Amount = s.Amount,
BranchName = s.Branch.Name,
ItemName = s.Item.Name,
SortageMargin = s.Item.ShortageMargin,
IsShort = s.IsShort
});
return storage;
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _dataContext.Set<TEntity>().Where(predicate);
}
I made sure the ItemId and BranchId are set to foreign keys for the Items and Branches tables in the database, they don't allow nulls and they enforce foreign key constraints
could anyone tell me why only the Branch get loaded while Item is always set to null
Generally you shouldn't try to wrap frameworks, particularly EF with helper methods like you're doing with your Find method, which I mention because that is why you're running into this problem (rather it's making the solution harder than it would otherwise have to be without the helper).
You may run into this problem you ran into (or where you want to specify AsNoTracking() in your Find one day when you find out about the ChangeTracker and how it works and SaveChanges() starts running slow or you have a large memory footprint due to having too much attached at once. Of course this is problem more with EF because it makes it too easy for people to develop without really understanding important concepts like transactions - the details of the layer it's trying to abstract away...)
You would need to load related entities somehow. I'd recommend reading this link and choosing an option. Here's one option on that link that only touches your GetStorage method, which might "solve the problem easily" but may not be good performance-wise if there are a lot of records - considering your predicate is including everything, though, there may not be a lot. I can't give you a better recommendation without seeing more of your code. If it's loading one and not the other and they're otherwise identical (same non-nullable FK and there's a corresponding record) then I think it's probably configuration somewhere... again read the link to see how to configure loading of navigation properties. Note this will probably generate multiple SELECT statements; I couldn't think of a better way (besides dropping down into Set<>) with the constraints of using Find inside GetStorage to grab the missing records you needed. If you could call the Find from a different class that has a different TEntity specified then you could probably get just the Items records you need in one SELECT statement - but I don't know how you're setting up your services and their lifecycles. That might be a compromise between performance and not having a mass-refactoring of the data access wrappers.
var allEntities = Find(x => true).ToList();
allEntities.ForEach(x => _dataContext.Entry(x).Reference(y => y. Item).Load());
var storage = allEntities.Select(s => new StorageViewModel
// ...
GetStorage seems like it's specific to one TEntity; Your Find method seems like it has TEntity defined as a generic in the containing class - so if I had to guess those methods are in (2) different classes even though you put them back-to-back. Your Find method is then probably on a class that abstracts away EF to give you a "simpler" interface to the database. EF is already that simple interface to the database; you don't need another.
What you could be doing instead is making concrete repositories that take a hard dependency on specific TEntity's and depend on a DbContext and then having either your domain logic taking a dependency on your repositories (which would require you to mock the repository somehow - either with a pseudo database, actual database or a leaky in-memory database to "unit" test your domain logic) or having your repositories totally de-coupled from your domain logic, enabling fast-executing domain logic unit tests. Then your repositories have shielded the rest of the application from EF, so you could change it out to ADO.NET or a lighter weight ORM like Dapper.
A Find method like that is an EF abstraction - you've tied whatever depends on that to EF (and your schema, probably) even if you didn't realize it. What I briefly described is true a database abstraction - which actually would free you up to change your data access (and schema) if you needed to - just change out the repository implementation without changing its API or behavior...

Entity Framework 5 and Code first - unable to add record to a table that has relation with other table(s)

I am working on an ASP.NET MVC4 application using EF 5 and Code First workflow. I have two entities:
public class Document
{
//other properties...
public int DocumentID { get; set; }
public virtual IList<UploadedFile> UploadedFiles { get; set; }
}
and:
public class UploadedFile
{
//other properties..
public int UploadedFileID { get; set; }
public int DocumentID { get; set; }
public virtual Document Document { get; set; }
}
In short - I have different kind of documents and each document may have 1 or more files associated with him. What I haven't thought about is that I'm going to have files that are not associated with a document. So I still need to save the file specific information and the UploadedFile entity has all the properties I need for that but I don't want to set values for the Document properties (leave them null). Which leads to the problem.
I already have some business logic written and if I change to public int? DocumentID { get; set; } - make the FK nullable I get compile errors from the code that I already have. Also, I might to decide and rewrite my code to reflect that change but I'm not sure if setting the FK to be nullable is best solution anyways.
The second solution that I can think of is to just create another entity OtherFiles (or something like this, just example name) where I will keep the records for the files that are not associated with some kind of document. Which will solve my problem in general but it seems like the worst solution since I'm gonna repeat all the properties of UploadedFile but without the relation to another table.
And third - if anyways I have to rewrite my code in order to keep some decent design (which I am trying to do) maybe there's a better way than setting the FK to null. I've watched a part of video tutorial by Scott Allen where he was setting two entities with the same structure, there it was Restaurant and RestaurantsReviews and in this video he didn't bother at all adding public virtual Restaurant Restaurant { get; set;} and had only public int RestaurantID { get; set; } saying that this is not mandatory but might be useful in some cases.
I don't know which cases are those, but maybe for my case it would be best just to remove DocumentID and virtual Document Document and rewrite my code? If this is the case what I lose as ability when I remove DocumentID from my UploadedFile entity?
Having the Id column and the virtual object to define the FK, helps when using lazy loading, and in many other ways. So if you have something like this:
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<RestaurantReviews> Reviews{ get; set; }
}
public class RestaurantReviews
{
public int Id{ get; set;}
public string Review { get; set; }
public int RestaurantId { get; set; }
public virtual Restaurant Restaurant { get; set; }
}
And for some reason you're working with the any Review and need the Restaurant's name, you just say:
var restaurantNameToShow = myReview.Restaurant.Name;
So EF is going to go again to the DB and get you the name (because he's lazy and didn't bring it in the first place). This answer your doubt about EF, regarding what option you should take, it's seems to me that is a very very complicated thing, and maybe a complicated solution ain't bad at all. For all the thing we spoke in the comments I'd choose the second option. If for some reason I get another idea on how solve it, I'm gonna tell it to you ;)

How to synchronize changes in nosql db (ravendb)

I've started learning NoSQL on an example of RavenDB. I've started with a simplest model, let's say we have topics that were created by users:
public class Topic
{
public string Id { get; protected set; }
public string Title { get; set; }
public string Text { get; set; }
public DenormalizedUser User { get; set; }
}
public class DenormalizedUser
{
public string Id { get; set; }
public string Name { get; set; }
}
public class User
{
public string Id { get; protected set; }
public string Name { get; set; }
public DateTime Birthdate { get; set; }
//some other fields
}
We don't need the whole User for displaying a Topic, so I've denormalized it to DenormalizedUser, containing an Id and a Name.
So, here are the questions:
1) Is this approach correct for NoSQL?
2) How to handle cases when User changes the Name? Do I need to manually update all the Name fields in denormalized classes?
Shaddix you can use the Raven DB Include function to load the User using the UserId from your topic.
var topic = _session.Load<Topic>(topicId)
.Customize(x => x.Include<Topic>(y => y.UserId));
var user = _session.Load<User>(topic.UserId);
The Load for Topic will 'preload' the User and both Loads will only result in one GET request. (I couldn't reply directly to your response to Ayende due to my reputation).
You also use the alternative (and probably clearer) .Include() function without Customize().
http://docs.ravendb.net/consumer/querying/handling-document-relationships.html
shaddix,
You don't need to denormalize, you can hold a reference to the id and then Include that when you load from the server
1) Yes, this approach works fine and the result is, that you only need to load the topic-document when you want to display it along with the name of its user. However, as Ayende states, the perfomance will be nearly the same as if you didn't denormalize the user and just include it when needed. If you don't worry about multiple-server deployment I recommend that approach.
2) If you really want to denormalize the user, then you can update all topics referencing this user simply with a set based operation. Look at this: http://ravendb.net/faq/denormalized-updates

search for int id starting with x entity framework 4.1

I currently have an Entity Framework model that collects data from a legacy database and I am currently using an int on my Id properties
I am attempting to build a search box with autocomplete capabilities and want to have the autocomplete function to return a subset of records based on whether the sample id either contains or starts with (final design decision not made yet) and I am running into problems with converting the integer id to a string as I would normally use a recs.Id.toString().StartsWith(recordId) but this is apparently not supported by the Entity Framework
Is there a way around this limitation ?
My code looks like the following
Model:
public class Sample
{
public Sample()
{
Tests = new List<Test>();
}
public int Id { get; set; }
public DateTime SampleDate { get; set; }
public string Container { get; set; }
public string Product { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual SamplePoint SamplingPoint { get; set; }
public virtual SampleTemplate SampleTemplate { get; set; }
public Customer ForCustomer { get; set; }
public virtual ICollection<Test> Tests { get; set; }
}
and the query I am currently trying to apply to this model
[HttpGet]
public JsonResult AutoComplete(string partialId)
{
var filteredSamples =
repo.AllSamples.Where( s =>
String.Compare(s.Status, "A", false) == 0
&& (s.Id.ToString()).StartsWith(partialId)
).ToList();
return Json(filteredSamples, JsonRequestBehavior.AllowGet);
}
Any ideas would be awesome I am out of ideas at this point
No matter what you do, this is going to result in some awful performance on large datasets, because you will not be able to use any indices. My recommendation would be to use a trigger or scheduled task to store the leading digit in a separate field and filter on that.
I ended up adding a view for autocomplete data and converting the data to string in the select statement and this solved my issue
Wild thought: how about your create a computed, persisted column on your database table, that converts your ID (INT) into a string?
Then you could:
put an index on that column
use a simple string comparison on that string column
Basically, you need this:
ALTER TABLE dbo.YourTable
ADD IDAsText AS CAST(ID AS VARCHAR(10)) PERSISTED
Now update you EF model - and now you should have a new string field IDAsText in your object class. Try to run your autocomplete comparisons against that string field.

Categories

Resources