Lazy Loading simple type nullable properties - c#

Is it possible to implement a lazy loading of a simple type property?
In some cases I do have classes that have rarely used properties.
Because of that, I don't want EF to select their values on the loading process. I wish to explicitly 'ask' for the loading of those values.
So far, I've been placing those properties on a separate class, 1-to-1 with the main class, and then Lazy loading that secondary class (that only holds the less used properties). But it really would be nicer to place all properties on the same class without the burden of loading them each and every time.
For example:
public class User {
[Key]
public int IDUser { get; set; }
[StringLength(20)]
public string Login { get; set; }
[StringLength(60)]
public string Nome { get; set; }
public DateTime Birth { get; set; }
[StringLength(200)]
public string Email { get; set; }
//Less used properties (to be Lazy-loaded):
public virtual string Address { get; set; }
public virtual int? Floor { get; set; }
public virtual int? DeskNumber { get; set; }
public virtual string SkillsDefinition { get; set; }
}

Related

Using Entity Framework for relating 3 entitites where 1 of them can only be related to either one of remaining two

Suppose I have two entities, bungalows and apartments. Both of them have varying fields and cannot be interchanged however both these entities have multiple tenants. Each tenant can only be part of either one bungalow or one apartment. How do I achieve this using Entity Framework?
I was thinking of creating 2 more entities bungalowTenants and apartmentTenants and using these to map. Each bungalowTenant would have one instance of a bungalow and a tenant and similarly for apartmentTenant.
Bungalows would have a collection of bungalowTenants and apartment of apartmentTenants.
public class Bungalow
{
public int Id { get; set; }
public int HouseNumber { get; set; }
public string Street { get; set; }
public ICollection<BungalowTenants> Tenants { get; set; }
}
public class Apartment
{
public int Id { get; set; }
public int ApartmentNumber{ get; set; }
public string Wing{ get; set; }
public string Building{ get; set; }
public ICollection<ApartmentTenants> Tenants { get; set; }
}
public class Tenant
{
public int Id{ get; set; }
public string Name{ get; set; }
}
public class ApartmentTenants
{
public int ApartmentId { get; set; }
public Apartment Apartment{ get; set; }
public int TenantId{ get; set; }
public Tenant Tenant{ get; set; }
}
public class BungalowTenants
{
public int BungalowId{ get; set; }
public Bungalow Bungalow{ get; set; }
public int TenantId{ get; set; }
public Tenant Tenant{ get; set; }
}
The problem with this approach is that it does not restrict in any way the same tenant to be a part of both, a bungalow and an apartment. I am unable to figure out how to do that using Entity Framework. I'd appreciate any help on this matter.
Not every business rule can be or needs to be translated to database constraints or model constraints.
And if you would enforce this, through model rules or programmed rules:
What if the tenants decide to move from a bungalow to an apartment? They will most probably want to start renting the new home days or even weeks before the cancellation date of the old one - or do you expect them to vacate the old home before midnight and enter the new home after midnight, with all their belongings packed in boxes on the pavement for a certain period? That does not seem very realistic.

Dummy query to make recursive EFCore 2 properties load

I'm tearing my hair out trying to get my recursive EFCore objects to load all their grandparents/grandchildren and I can't decide if I'm being rubbish or I'm working around features which aren't working as desired.
A Link has a Category, which exists in a hierarchy of other Categories (the data structure works fine)
Link link = FP.Links.Include(a=>a.Transaction.Account.Home)
.Include(a=>a.Category)
.ThenInclude(b=>b.parent)
.AsEnumerable()
.Where(a=>a.TransId==_transId &&
a.Transaction.Account.Home==_authHome)
.FirstOrDefault();
When I look at the Link.Category.parent.parent it is null. It always seems to load the first couple of levels above, when I pull back a link whose category has more than 2 parents, it eventually gives me a null.
I've tried various SO answers which have guided me to use includes (which feels clunky) and the latest talks about .AsEnumerable(). I've tried FirstOrDefault() and SingleOrDefault().
I've got as far as: Loading in EFCore 2 is far from done, and that 'fixing up' is a thing. So, I tried adding:
List<Category> CL= FP.Categories.Where(a=>a.home==_authHome).ToList();
before the previous query and it works.
I have three questions:
1) Have I correctly interpreted that this forces my DBContext to have already loaded the data I need, and so it has access when I re-query?
2) Am I leaving myself open to this NOT working in some circumstances which testing wouldn't show me?
3) Is there any less inane way to make this same thing work?
(I may want link.Category.parent.parent.parent.(n).parent to all be accessible)
and a question that, knowing the rules of SO, I'll phrase rhetorically:
4) Is there any world in which this is desirable from a debugging/coding point of view????
Classes:
public class Link
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
[ForeignKey("Transaction")]
public int TransId { get; set; }
public DateTime effectiveDt { get; set; }
public bool isRefund { get; set; }
public int transactionRole { get; set; }
public int setBy { get; set; }
public string transactionComment { get; set; }
public virtual Transaction Transaction { get; set; }
public virtual Category Category { get; set; }
}
public partial class Category
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
public string Name { get; set; }
public int categoryType { get; set; } //0=Normal, 1=Savings, 2=Loan
[JsonIgnore]
public virtual Category parent { get; set; }
[JsonIgnore]
public virtual ICollection<Summary> summaries { get; set; }
public virtual ICollection<Category> children { get; set; }
[JsonIgnore]
public virtual ICollection<Link> Link { get; set; }
[JsonIgnore]
public virtual ICollection<Budget> Budget { get; set; }
[JsonIgnore]
public virtual Home home { get; set; }
}

Is there a way to control the order of properties in JSON objects?

I'm using Entity Framework Core, and the generated class has its own properties, i.e.
DataModel.Agent.cs
public partial class Agent {
public virtual decimal Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
}
But I need other properties, so I declare them in another file:
Agent.cs
public partial class Agent
{
[NotMapped]
public dynamic Custom { get; set; }
}
The problem is that Agent.cs is compiled before DataModel.Agent.cs, so the compiler generates properties in this order: Custom, Id, Name, and the resulting JSON is weird.
I want it to be: Id, Name, Custom. In other words, I always want the DataModel class to come first.
EDIT: Just to clarify, the only objective is to make the JSON prettier by always putting the Id first, which is a very common pattern. This has absolutely no impact on how the application works.
Is there a way to force the compiler to always compile one of the files first?
Well you really shouldn't count on JSON property order BUT if using json.net
public class Account
{
public string EmailAddress { get; set; }
// appear last
[JsonProperty(Order = 1)]
public bool Deleted { get; set; }
[JsonProperty(Order = 2)]
public DateTime DeletedDate { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
// appear first
[JsonProperty(Order = -2)]
public string FullName { get; set; }
}
http://www.newtonsoft.com/json/help/html/JsonPropertyOrder.htm

C# MVC Code First Complex Model

I have one table "Adverts" which stores basic info about adverts (eg: Name, Excerpt, Creation date...), and I need to store more detailed info in a separate table, But, here's my problem. Adverts can be different by type (sell, buy, rent, ...), category (residential, commercial, ...), so, detailed info is also different (eg: Commercial Advert don't need kitchen area property). I want to make few models which will describe detailed info for specific type or category
Here's my Adverts model:
[Table("Adverts_Adverts")]
public class Advert {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AdvertId { get; set; }
public virtual Metadata Metadata { get; set; }
[Required]
[DataType(DataType.Text)]
public String Name { get; set; }
[DataType(DataType.Html), AllowHtml]
public String Content { get; set; }
[ForeignKey("Section")]
public Guid SectionId { get; set; }
public virtual Section Section { get; set; }
[ForeignKey("Category")]
public Guid CategoryId { get; set; }
public virtual Category Category { get; set; }
[ForeignKey("Type")]
public Guid TypeId { get; set; }
public virtual Type Type { get; set; }
public Decimal Price { get; set; }
[DataType("Enum")]
public Currency Currency { get; set; }
[ForeignKey("Details")]
public Guid DetailsId { get; set; }
public virtual ?????????? Details { get; set; }
[ForeignKey("User")]
public String UserId { get; set; }
public virtual User User { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Added { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Updated { get; set; }
public Int32 Views { get; set; }
[ReadOnly(true)]
public Status Status { get; set; }
...
}
here's my detailed info model for residential adverts:
[Table("Adverts_Details")]
public class ResidentialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 Storeys { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Int32 Rooms { get; set; }
[Required]
public Decimal TotalArea { get; set; }
[Required]
public Decimal LivingArea { get; set; }
[Required]
public Decimal KitchenArea { get; set; }
...
}
and this may be for commercial adverts:
[Table("Adverts_Details")]
public class CommercialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 OfficesCount { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Decimal TotalArea { get; set; }
...
}
So, how can I access both, ResidentialDetails and CommercialDetails, data within advert's property "Details"?
(Thank in advance)
This is an architecture problem, which is hard to answer without a complete understanding of your business rules. I can give you some general advice that will hopefully help you along.
As much as possible, remove complexity. I'm not sure what a "kitchen area property" is, but can you generalize it at all? Based upon context, you can call it something different, use it differently, etc. but if it's just a text field, then you can repurpose it in other contexts. Maybe for a residential advert it's "kitchen area" while maybe for commercial it's "break room area". (I really have no idea what this property is for, but I'm just trying to make the point that the same property can have a similar but slightly different meaning in different contexts).
If you can't generalize, then you'll need to start working on inheritance strategies. Create an object graph. How are these types and categories of adverts related. How are they different. Which ones are supergroups of others, etc.? Again, I don't know anything about the business rules at play, but maybe you need classes like Advert, ResidentialAdvert : Advert and CommercialAdvert : Advert. Then, you can add additional properties to these subclasses as necessary.
You'll also need to decide on a relational strategy. By default, EF will implement simple inheritance as STI (single-table inheritance, aka table per hierarchy or TPH for short). In other words, with the classes above, you would end up with an Adverts table with a Discriminator column. The value for this column would be one of "Advert", "ResidentalAdvert", or "CommercialAdvert", indicating which class should be instantiated, but all of the columns for all of the subclasses would reside in the same table. The benefit is that no joins are necessary, but the detriment is that all additional columns on your subclasses must be nullable or have default values. Other possible strategies would include, table per type (TPT), a compositional strategry, or table per concrete type (TPC), where every subtype gets its own unique table with all the fields from all supertypes.

How to architect Entity Framework Application (with MEF)

I am desperate to find out how to architect my Entity Framework 4 (code first) application.
I have one VS project that will handle access to my data. Its a MEF-Exported Part [MyData], based on an Interface [IDataExport]. That project has my EF classes (customer, order, etc), the context initializer, etc and all that already works like a dream.
I have one VS project that has my interfaces (all my interfaces). All projects have a reference to this Interface project.
I have one VS project that does all my logging. It is also a MEF-Exported Part [MyLog], based on an interface [ILogging]. That class really just writes to the Console.
I have Three VS projects that we will call Parts (in MEF terms). They are plugins. They need data to work (customers, orders, etc). Actually, they need data as an Input from three different tables, all at once.
I have one project that is the Host application. It is currently running as a console application but will soon be converted to a Windows Service.
I hope that gave you a good idea of the architecture that is in place. Now I am having troubles trying to figure out how to do my data access correctly.
When the host needs data to pass to the plugins, it needs to get data from 3 different tables. Actually, the way it is setup with EF, the three tables will be retrieved at once. How do I pass that data to the plug-in, when the plugin was instantiated by MEF? Can Plug-Ins raise events to interact with the Host application?
In addition, as the plug-ins run, data in the tables will need to be updated. How do I keep my data in the database updated three layers up? The Host can call the Plug-In, but the Plugin doesn't have a way to call the Host. Only the [MyData] project has access to the Database.
Based on the scenario that I described, could someone please tell me how to best architect this application?
Adding further to my confusion, some sample code shows the calling application (in this case the host), starting brand new Models for each search call to the database. e.g.
public List<Customer> FindCustomerList(string companyName)
{
return new CustomerManager().FindCustomerList(companyName);
}
public List<Customer> FindCustomerList(string companyName)
{
var q = from c in context.Customers
where c.CompanyName.StartsWith(companyName)
select c;
return q.ToList();
}
Below are my three tables. Please note that they have foreign key relationships, resulting in sub-items being embedded inside of the main job record. Like a customer with many orders.
public class pcJobAction : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
[Required]
[MaxLength(30)]
public string ServerName { get; set; }
[MaxLength(20)]
public string ServerIP { get; set; }
public int JobEnabled { get; set; }
public virtual ICollection<pcPlugInValue> PlugInText { get; set; }
//JobActions holds a list of Schedules
public virtual ICollection<pcJobSchedule> JobSchedules { get; set; }
//FK to the JobTypes table (Delete Files, Verify Backups, Ping, etc)
public long pcJobTypeId { get; set; }
public virtual pcJobType pcJobType { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class pcPlugInValue : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
public string PlugInText { get; set; }
public int ExecuteOrder { get; set; }
//FK to the JobAction table
public long pcJobActionId { get; set; }
public virtual pcJobAction pcJobAction { get; set; }
//FK to the codes table (to indetify the schedule type: daily, weekly, etc)
public long pcCodeId { get; set; }
public virtual pcCode pcCode { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class pcJobSchedule : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
//FK to the JobAction table
public long pcJobActionId { get; set; }
public virtual pcJobAction pcJobAction { get; set; }
//FK to the codes table (to indetify the schedule type: daily, weekly, etc)
public long pcCodeId { get; set; }
public virtual pcCode pcCode { get; set; }
public DateTime StartDate { get; set; }
public Boolean dayMonday { get; set; }
public Boolean dayTuesday { get; set; }
public Boolean dayWednesday { get; set; }
public Boolean dayThursday { get; set; }
public Boolean dayFriday { get; set; }
public Boolean daySaturday { get; set; }
public Boolean daySunday { get; set; }
public Boolean ThisJobIsNext { get; set; }
public DateTime EndDate { get; set; }
public int DateOfMonth { get; set; }
public int DayOfWeek { get; set; }
public DateTime ScheduleHour { get; set; }
public int EveryHowMany { get; set; }
public DateTime RunTimeLast { get; set; }
public DateTime RunTimeNext { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
From your architecture description, can I assume that your host application has, somewhere, an [ImportMany] that causes all of your plugins to be instantiated by MEF?
If that is the case, one option is (as I believe you asked) to add an event to your plugin interfaces and attach to that event in each plugin from your host application. I have done that myself and it works fine.
Another option, if it fits into your architecture, is to put your EF classes in a separate assembly, reference that assembly in your plugin assemblies, and do your data access directly from the plugins.
I've done the second option myself, where I have placed my EF code-firstclasses into a seperate assembly, and have some helper classes that are used to connect to the contextclass, and query the ef repository.
However, if you don't want your plugins to have direct access to the entire database, then its probably best to do option 1. Especially if in the future you decided to have your database tables split into different schemas, and you want only certain plugins to be only to interact with specific schema within your database.

Categories

Resources