I am retrieving data from the database (with Entity Framework) using DTOs:
IQueryable<User> users = repository.ListFiltered<User>(n => n.Active);
var usersDTO = from user in users
select new UserAccountDTO{
UserId = user.Id,
UserName = user.Name,
InstitutionName = user.Institution.Name,
InstitutionCountry = user.Institution.Country.Name
};
I'm doing this because the user entity, and the institution entity, have a lot of data i don't need right now, so i don't want to retrieve it from the database.
But the problem is that i don't like the code, i would like to split the code and concatenate the selects, is there anyway to do this?
I'd like to arrive to something like:
users.LoadUserData().LoadInstitutionData();
What do you say? is it possible?
The problem is what LoadUserData() and LoadInstitutionData() should look like. Let's say that the end product of this method chain should be an IEnumerable<UserAccountDTO>. The last method in fluent syntax always determines the output. So LoadInstitutionData() should return the required IEnumerable. Clear.
But what about the input? I see two alternatives:
The input is IEnumerable<User> (or IQueryable<User>). OK, that would mean that LoadInstitutionData() can't do anything else than the code you already have. And that's not a solution, because you don't like the code. Moreover, the method requires that user.Institution and the Country must be loaded or lazy loadable, which is hard to express in any form of code contract.
The input is IEnumerable<UserAccountDTO> in which LoadUserData has set the direct user properties and that LoadInstitutionData should replenish with Institution data. Now the question is: how should LoadInstitutionData do that? Anyhow, what was done in one query now will take at least two queries, maybe even 1 + n.
More complex alternatives could consist of passing and composing expressions, but surely you'll be jumping from the frying pan into the fire, reinventing LINQ's extension methods, or AutoMapper, or...
Wait, AutoMapper.
Did you know that AutoMapper could do this for you in a way that may appeal to you?
All you need is a class
class UserAccountDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string InstitutionName { get; set; }
public string InstitutionCountryName { get; set; } // Notice the Name part!
};
A mapping
AutoMapper.Mapper.CreateMap<User, UserAccountDTO>();
A using:
using AutoMapper.QueryableExtensions;
And a concise syntax:
var usersDTO = users.Project().To<UserAccountDTO>();
(And AutoMapper from NuGet, of course).
Automapper will resolve a property like InstitutionCountryName as user.Institution.Country.Name and the result of the projection is one expression that is translated into one SQL statement with joins and all. That's code I do like.
Related
I'm getting the following content when I invoke my API. It kind of breaks up in the middle when the tenant entity that member is linked to, will start listing its member entities.
{
"id":"00000000-7357-000b-0001-000000000000",
"tenantId":"00000000-7357-000a-0001-000000000000",
"userName":"user1",
"tenant":{
"id":"00000000-7357-000a-0001-000000000000",
"name":"First Fake Org",
"members":[
I configured the lazy loading like this.
services.AddDbContext<Context>(config => config
.UseLazyLoadingProxies()
.UseSqlServer(Configuration.GetConnectionString("Register")));
How should I change the code so that the lazily loaded entities don't get served? I was hoping that it would simply return an empty list to the client. Should I use a DTO for that purpose and not return from the DB like this? There's talk about not using lazy loading for APIs at all here.
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
Member output;
output = Context.Members
.Include(e => e.Tenant)
.Single(e => e.UserName == userName);
return Ok(output);
}
I'm not sure what to google for and all the hits I got were pointing to the UseLazyLoadingProxies() invokation.
This will probably be somewhat long winded: But here goes.
It sounds like you have Entities which look something like:
public partial class Member
{
public virtual long Id { get; set; }
public virtual List<Tenant> Tenants { get; set; } //tables have fk relationship
}
public partial class Tenant
{
public virtual long Id { get; set; }
public virtual List<Member> Members{ get; set; } //tables have another fk relationship?
}
And then for this method:
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
Member output;
output = Context.Members
.Include(e => e.Tenant)
.Single(e => e.UserName == userName);
return Ok(output);
}
I see a few issues, but I'll try to keep it short:
I wouldn't have the controller do this directly. But it should work.
What I think you over looked is exactly what the .Include statement does. When the object is instantiated, it will get all of those related entities. Includes essentially converts your where statement to a left join, where the foreign keys match (EF calls these navigation properties).
If you don't want the Tenant property, then you can omit the .Include statement. Unless this is meant to be more generic (In which case, an even stronger reason to use a different pattern and auto mapper).
Hopefully your database doesn't truly have a FK relationship both ways, if it does, fix that ASAP.
The next issue is that you might not want a list of child properties, but it is in the model so they will be "there". Although your List Tenants might be null. And while this might be fine to you, right now. As a general rule when I see an API returning a property, I expect something to be either not there (This Member doesn't have tenants) or something is wrong, like perhaps there is a second parameter I missed. This probably isn't a problem 93.284% of the time, but it is something to be mindful of.
This starts to get into why an AutoMapper is great. Your Database Models, Business Models and views are likely different. And as much as you shouldn't return the database models directly. Taking control of how the data is represented for each part of the application is a great idea.
You could reduce the code easily, and remove the navitation properties:
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
return Ok(Context.Members
.Include(e => e.Tenant)
.Single(e => e.UserName == userName));
}
But again, a business layer would be better:
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
return Ok(MemberRepository.GetMember(userName));
}
The main point I'd stress though, is creating a view model.
For example, Let's say a user Detail:
public class MemberDetail
{
public string UserName {get; set;}
public long UserId { get; set; }
public string FullName { get; set; }
}
This way the view always receives exactly what you want to see, and not the extra data. Add this with the fact that you can know exactly how every use of Member to MemberDetail will map.
I have a question and I'm struggling to find the answer.
I have 3 Entity classes with a relation like that (simplified):
class Article {
public int id
public string Name
public int ArticleStandard Id
public int CompanyId
public ArticleStandard ArticleStandardNavigation
public Company CompanyNavigation
}
class ArticleStandard {
public int id
public ICollection<Article> Articles
}
class Company {
public in id
public ICollection<Article> Articles
}
So the relation is 1 company can have many articles, 1 standardarticle can have many articles. They are all setup as DBSets in the DBContext.
Given is the Id of the ArticleStandard. I now want to get all companies who have this standard article through the article entity.
The TSQL I would like to "produce" is:
select c.* from Company c
inner join Article a on c.ID = a.CompanyId
inner join ArticleStandard on a.ArticleStandard = arts.id
Where arts.Id = 1
Which gives the result I want to have.
I tried a lot around and I don't want to post all my trials to work around the issue, that could make it work with just Include and ThenInclude. But I dont want to get all Articles and ArticleStandard. If I only select the company the Includes are ignored:
https://learn.microsoft.com/en-us/ef/core/querying/related-data#ignored-includes
var vtp = context.Company.Include(a => a.Article).ThenInclude(ars => ars.ArticleStandardNavigation).ToList();
Here I also struggle to even use a where clause on the included entities.
I read about that issue here:
How to add where clause to ThenInclude
But I couldnt make it really work, especially only retrieving the company and not the other entities.
I know I could load all and just take the companies out. That would work. But I want to reduce the amount of data sent and also keep the query as one.
Any hints? I'm still pretty inexperienced with EF Core and LinQ is also still sometimes confusing for me.
If you need more information or can point me to a similar problem (I found some kind of similar cases, but couldn't use them correctly) I would be very thankful.
Thank you for your time.
Well, forget about SQL and joins. In EF (Core) targeting LINQ queries you use the navigation properties to access the related data. In the context of the query, the entities represent the database table records and navigations - joins and related table records.
For reference navigation properties you use simple criteria like:
Where(entity => entity.Reference.SomeProperty == someValue)
and for collection navigation properties, usually Any with the criteria needed, e.g.:
Where(entity => entity.Collection.Any(related => related.SomeProperty == someValue))
in other words, I want records having at least one related record with this value.
Applying the above rules to your model, the equivalent query would be like this:
var query = db.Companies
.Where(c => c.Articles.Any(a => a.ArticleStandardNavigation.id == 1));
The generated SQL most likely will not be the same as the one you would write by hand (in general we cannot control the SQL generation of the ORM), but the result should be.
Btw, there is no need to append Navigation to the navigation property names. The default (and more intuitive) convention is to use the class name, e.g.
public class Article
{
// ...
public ArticleStandard ArticleStandard { get; set; }
public Company Company { get; set; }
}
I have following repository. I have a mapping between LINQ 2 SQL generated classes and domain objects using a factory.
The following code will work; but I am seeing two potential issues
1) It is using a SELECT query before update statement.
2) It need to update all the columns (not only the changed column). This is because we don’t know what all columns got changed in the domain object.
How to overcome these shortcomings?
Note: There can be scenarios (like triggers) which gets executed based on specific column update. So I cannot update a column unnecessarily.
REFERENCE:
LINQ to SQL: Updating without Refresh when “UpdateCheck = Never”
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=113917
CODE
namespace RepositoryLayer
{
public interface ILijosBankRepository
{
void SubmitChangesForEntity();
}
public class LijosSimpleBankRepository : ILijosBankRepository
{
private IBankAccountFactory bankFactory = new MySimpleBankAccountFactory();
public System.Data.Linq.DataContext Context
{
get;
set;
}
public virtual void SubmitChangesForEntity(DomainEntitiesForBank.IBankAccount iBankAcc)
{
//Does not get help from automated change tracking (due to mapping)
//Selecting the required entity
DBML_Project.BankAccount tableEntity = Context.GetTable<DBML_Project.BankAccount>().SingleOrDefault(p => p.BankAccountID == iBankAcc.BankAccountID);
if (tableEntity != null)
{
//Setting all the values to updates (except primary key)
tableEntity.Status = iBankAcc.AccountStatus;
//Type Checking
if (iBankAcc is DomainEntitiesForBank.FixedBankAccount)
{
tableEntity.AccountType = "Fixed";
}
if (iBankAcc is DomainEntitiesForBank.SavingsBankAccount)
{
tableEntity.AccountType = "Savings";
}
Context.SubmitChanges();
}
}
}
}
namespace DomainEntitiesForBank
{
public interface IBankAccount
{
int BankAccountID { get; set; }
double Balance { get; set; }
string AccountStatus { get; set; }
void FreezeAccount();
}
public class FixedBankAccount : IBankAccount
{
public int BankAccountID { get; set; }
public string AccountStatus { get; set; }
public double Balance { get; set; }
public void FreezeAccount()
{
AccountStatus = "Frozen";
}
}
}
If I understand your question, you are being passed an entity that you need to save to the database without knowing what the original values were, or which of the columns have actually changed.
If that is the case, then you have four options
You need to go back to the database to see the original values ie perform the select, as you code is doing. This allows you to set all your entity values and Linq2Sql will take care of which columns are actually changed. So if none of your columns are actually changed, then no update statement is triggered.
You need to avoid the select and just update the columns. You already know how to do (but for others see this question and answer). Since you don't know which columns have changed you have no option but set them all. This will produce an update statement even if no columns are actually changed and this can trigger any database triggers. Apart from disabling the triggers, about the only thing you can do here is make sure that the triggers are written to check the old and new columns values to avoid any further unnecessary updates.
You need to change your requirements/program so that you require both old and new entities values, so you can determine which columns have changed without going back to the database.
Don't use LINQ for your updates. LINQ stands for Language Integrated QUERY and it is (IMHO) brilliant at query, but I always looked on the updating/deleting features as an extra bonus, but not something which it was designed for. Also, if timing/performance is critical, then there is no way that LINQ will match properly hand-crafted SQL.
This isn't really a DDD question; from what I can tell you are asking:
Use linq to generate direct update without select
Where the accepted answer was no its not possible, but theres a higher voted answer that suggests you can attach an object to your context to initiate the change tracking of the data context.
Your second point about disabling triggers has been answered here and here. But as others have commented do you really need the triggers? Should you not be controlling these updates in code?
In general I think you're looking at premature optimization. You're using an ORM and as part of that you're trusting in L2S to make the database plumbing decisions for you. But remember where appropriate you can use stored procedures execute specific your SQL.
Its probarbly a simple 3-tier problem. I just want to make sure we use the best practice for this and I am not that familiary with the structures yet.
We have the 3 tiers:
GUI: ASP.NET for Presentation-layer (first platform)
BAL: Business-layer will be handling the logic on a webserver in C#, so we both can use it for webforms/MVC + webservices
DAL: LINQ to SQL in the Data-layer, returning BusinessObjects not LINQ.
DB: The SQL will be Microsoft SQL-server/Express (havent decided yet).
Lets think of setup where we have a database of [Persons]. They can all have multiple [Address]es and we have a complete list of all [PostalCode] and corresponding citynames etc.
The deal is that we have joined a lot of details from other tables.
{Relations}/[tables]
[Person]:1 --- N:{PersonAddress}:M --- 1:[Address]
[Address]:N --- 1:[PostalCode]
Now we want to build the DAL for Person. How should the PersonBO look and when does the joins occure?
Is it a business-layer problem to fetch all citynames and possible addressses pr. Person? or should the DAL complete all this before returning the PersonBO to the BAL ?
Class PersonBO
{
public int ID {get;set;}
public string Name {get;set;}
public List<AddressBO> {get;set;} // Question #1
}
// Q1: do we retrieve the objects before returning the PersonBO and should it be an Array instead? or is this totally wrong for n-tier/3-tier??
Class AddressBO
{
public int ID {get;set;}
public string StreetName {get;set;}
public int PostalCode {get;set;} // Question #2
}
// Q2: do we make the lookup or just leave the PostalCode for later lookup?
Can anyone explain in what order to pull which objects? Constructive criticism is very welcome. :o)
You're kind of reinventing the wheel; ORMs already solve most of this problem for you and you're going to find it a little tricky to do yourself.
The way ORMs like Linq to SQL, Entity Framework and NHibernate do this is a technique called lazy loading of associations (which can optionally be overriden with an eager load).
When you pull up a Person, it does not load the Address until you specifically ask for it, at which point another round-trip to the database occurs (lazy load). You can also specify on a per-query basis that you want the Address to be loaded for every person (eager load).
In a sense, with this question you are basically asking whether or not you should perform lazy or eager loads of the AddressBO for the PersonBO, and the answer is: neither. There isn't one single approach that universally works. By default you should probably lazy load, so that you don't do a whole lot of unnecessary joins; in order to pull this off, you'll have to build your PersonBO with a lazy-loading mechanism that maintains some reference to the DAL. But you'll still want to have the option to eager-load, which you'll need to build into your "business-access" logic.
Another option, if you need to return a highly-customized data set with specific properties populated from many different tables, is to not return a PersonBO at all, but instead use a Data Transfer Object (DTO). If you implement a default lazy-loading mechanism, you can sometimes substitute this as the eager-loading version.
FYI, lazy loaders in data access frameworks are usually built with the loading logic in the association itself:
public class PersonBO
{
public int ID { get; set; }
public string Name { get; set; }
public IList<AddressBO> Addresses { get; set; }
}
This is just a POCO, the magic happens in the actual list implementation:
// NOT A PRODUCTION-READY IMPLEMENTATION - DO NOT USE
internal class LazyLoadList<T> : IList<T>
{
private IQueryable<T> query;
private List<T> items;
public LazyLoadList(IQueryable<T> query)
{
if (query == null)
throw new ArgumentNullException("query");
this.query = query;
}
private void Materialize()
{
if (items == null)
items = query.ToList();
}
public void Add(T item)
{
Materialize();
items.Add(item);
}
// Etc.
}
(This obviously isn't production-grade, it's just to demonstrate the technique; you start with a query and don't materialize the actual list until you have to.)
I am using Fluent NHibernate and having some issues getting a many to many relationship setup with one of my classes. It's probably a stupid mistake but I've been stuck for a little bit trying to get it working. Anyways, I have a couple classes that have Many-Many relationships.
public class Person
{
public Person()
{
GroupsOwned = new List<Groups>();
}
public virtual IList<Groups> GroupsOwned { get; set; }
}
public class Groups
{
public Groups()
{
Admins= new List<Person>();
}
public virtual IList<Person> Admins{ get; set; }
}
With the mapping looking like this
Person: ...
HasManyToMany<Groups>(x => x.GroupsOwned)
.WithTableName("GroupAdministrators")
.WithParentKeyColumn("PersonID")
.WithChildKeyColumn("GroupID")
.Cascade.SaveUpdate();
Groups: ...
HasManyToMany<Person>(x => x.Admins)
.WithTableName("GroupAdministrators")
.WithParentKeyColumn("GroupID")
.WithChildKeyColumn("PersonID")
.Cascade.SaveUpdate();
When I run my integration test, basically I'm creating a new person and group. Adding the Group to the Person.GroupsOwned. If I get the Person Object back from the repository, the GroupsOwned is equal to the initial group, however, when I get the group back if I check count on Group.Admins, the count is 0. The Join table has the GroupID and the PersonID saved in it.
Thanks for any advice you may have.
The fact that it is adding two records to the table looks like you are missing an inverse attribute. Since both the person and the group are being changed, NHibernate is persisting the relation twice (once for each object). The inverse attribute is specifically for avoiding this.
I'm not sure about how to add it in mapping in code, but the link shows how to do it in XML.
#Santiago I think you're right.
The answer might just be that you need to remove one of your ManyToMany declarations, looking more at Fluent it looks like it might be smart enough to just do it for you.
Are you making sure to add the Person to the Groups.Admin? You have to make both links.
You have three tables right?
People, Groups, and GroupAdministrators
when you add to both sides you get
People (with an id of p1)
Groups (with an id of g1)
and in GroupAdministrators you have two columns and a table that has
(p1,g1)
(p1,g1)
and your unit test code looks like the following.
Context hibContext //Built here
Transaction hibTrans //build and start the transaction.
Person p1 = new Person()
Groups g1 = new Groups()
p1.getGroupsOwned().add(g1)
g1.getAdmins().add(p1)
hibTrans.commit();
hibContext.close();
And then in your test you make a new context, and test to see what's in the context, and you get back the right thing, but your tables are all mucked up?