I'm not sure if this is possible with the Entity Framework but I'm duplicating too much code filling objects.
Currently my methods do something like:
return (from s in context.some_table
join u in context.user_table on s.user_id equals u.id
where s.id == someId
select new MyObject()
{
TableColumn = s.some_column,
User = new MyUser()
{
Username = u.username,
Id = u.id,
}
}.FirstOrDefault();
I just typed that out as an example. I have a lot of queries where I include info about the user (more than just in that example). So in all of my queries I'm putting that same chunk of code in. If I have 100 methods with that chunk of code and I want to add another column that's returned then I have to update 100 methods. Pain in the butt.
What I'd like to do is have the User object filled by a re-usable method. That way adding/removing columns returned only needs to be changed in one place. Like:
return (from s in context.some_table
join u in context.user_table on s.user_id equals u.id
where s.id = someId
select new MyObject()
{
TableColumn = s.some_column,
User = FillUser(u)
}.FirstOrDefault();
FillUser would be the method. Of course this doesn't work with Entity Framework.
Is there anything I can do at all? I could of course return the whole user table but that's way more info than I need so it's a waste.
I'm using .NET 4.0 if that helps.
Entity framework is ORM tool so its main purpose is to define mapping from database to objects and load entities based on that mapping. Obviously if you need 100 times same user projection it is scenario for either mapped database view, custom defining query or custom query view. You are abusing projections instead of creating reusable mapping.
Related
The following code works fine in LinqPad:
(from u in AspNetUsers
let userOrders = (from uo in UserOrders where uo.UserId == new Guid(u.Id) select uo)
select u)
However, when I try to execute the exact same code from my MVC application, it gives me the following error:
var users = (from u in ctx.Users
let userOrders = (from uo in ctx.UserOrders where uo.UserId == new Guid(u.Id) select uo)
select u);
Only parameterless constructors and initializers are supported in LINQ to Entities.
I don't recall ever having any issues converting a string to a GUID in Linq to Entities before. Am I missing some reference?
I have already included all the references I could think of:
using System.Linq;
using System.Data.Entity;
Why does it work in LinqPad but not in MVC?
Edit: Oddly enough, this seems to work:
let userOrders = (from uo in ctx.UserOrders where uo.UserId.ToString() == u.Id select uo)
When you are inside a context the object is not realized yet. Why are you not just using the auto built in navigation properties anyways?
When you have a foreign key in a database or data structure. That creates a 'navigation' property in Entity Framework. As such you can get to child objects a whole lot more quickly. A lot of times they are automatically given depending on your EF options of lazy versus eager loading. The 'include' forces that navigation to be obtained. So if just wanted the orders (seperate table) from my person table I would interrogate person and then get it's child table. I know also there is the concept of 'realized' with Entity Framework and until you can legitimately put something 'ToList' or 'ToString' where if you try to wire things up too much under the hood before they are realized they will not work.
static void Main(string[] args)
{
using (var context = new TesterEntities())
{
var peopleOrders = context.tePerson.Include("teOrder").First(p => p.PersonId == 1).teOrder.ToList();
peopleOrders.ForEach(x => Console.WriteLine($"{x.OrderId} {x.Description}"));
}
}
Ultimately, I just decided to go with the easier approach of casting my GUID .ToString() like so:
let userOrders = (from uo in ctx.UserOrders where uo.UserId.ToString() == u.Id select uo)
The reason being, the default implementation of AspNetUsers defines the column as being varchar, not uniqueidentifier, so in order to change it I would likely have to do something like this How to make EF-Core use a Guid instead of String for its ID/Primary key which I am not presently interested in doing!
So I have two tables, A and B. Each table is set up like so:
A: ItemAId, ItemAProperty1, ItemAProperty2, etc.
B: ItemBId, ItemBProperty1, ItemBProperty2, etc.
I have a record in table A and a record in table B. These records are in no way related to each other.
I have a third table, C set up like so:
C: ItemAId, ItemBId
I want to make a new record in table C from existing records in tables A and B. C is not available using intellisense. Everywhere I've looked, the suggestion is to build an object from A that already exists, and object(s) from B that already exist, add B to A (or vice versa), then do an add... and because the objects already exist in the database, EF will just do an update and link the objects together in the intermediate table. (From Insert/Update Many to Many Entity Framework . How do I do it?) I.E.:
/**** Rough Psuedocode ****/
var a = context.First(a => a.Id == passedInAId);
var b = context.First(b => b.Id == passedInBId);
a.BProperty.Add(b);
context.Add(a);
context.SaveChanges()
However, in this case, the objects have no properties that relates them so this is not working.
I know I can write a stored proc to do this, but is it possible to do with EF?
So, as I discovered from digging around some more...
Even though ItemA and ItemB have no direct relations with each other, it appears that the fact that there is a linking table which contains the primary IDs results in an ICollection of ItemA inside the ItemB object, and vice versa. So to get it to work, really all I needed to do was, in fact, get the two objects I wanted to link, add one to the other's collection of said objects, and do an add. Actual (imitation) code of how it works is:
using ( var ctx = new Model())
{
var item1 = ctx.ItemA.FirstOrDefault(i => i.Id == itemAId);
var item2 = ctx.ItemB.FirstOrDefault(j => j.Id == itemBId);
item1.ItemBs.Add(item2);
ctx.SaveChanges();
}
Of course, First() would work if the existence of the objects was already verified, and the "item1.ItemBs.Add()" has "ItemBs" because of EF's annoying tendency to pluralize EVERYTHING.
As a simplified example I have users, products and customers. Users are allowed access to certain products and to certain customers.
I'm using an edmx-file to map my SQL Server to my code and get the data using linq. A typical query might look something like this:
from prod in ctx.Products
join userProduct in ctx.UserProduct
on prod.Id equals userProduct.ProductId
join user in ctx.UserProfile
on userProduct.UserId equals user.Id
where user.UserName == username // <-- username is a method parameter
select new Product
{
Id = prod.Id,
DisplayText = prod.UserFriendlyText
}
Every time I need data from the database I must join towards the access rights table to EXCLUDE data the user does not have access to. This means that if someone (and it will happen eventually) forget to join towards the access table a user will see too much. Is there a way to INCLUDE data instead so that if I forget the access tables nothing is shown?
I've also been thinking about separating the different customers into different databases as their data will never be related to each other and it will be a small disaster if I leak data between customers. Leaking products between users from the same customer is bad but not as critical.
If it matters I'm in a C# MVC4 CQRS architecture with eventual consistency between the read and write side.
I've checked stack overflow for similar questions but all I could find was this unanswered one:
Access rules in CQRS read model
How about using the Repository pattern, and forcing your Dev's to use it to make calls to the Database? This will promote code reuse and improve the maintainability of the app.
Because a method will be called from the repository you can control the code that interacts with the database, and force consistency, that way you can make sure that the access table is always used, and used as you wish.
I have a similar problem in my database. 90% of my entities are "organisation dependent". My approach uses a generic base repository with methods like this:
public virtual T Find(int id)
{
T e = Context.Set<T>().Find(id);
var od = e as OrganisationDependent;
if (od != null && od.OrganisationID != CurrentOrganisationID)
return null;
if (e == null)
return null;
return e;
}
The "All" method was a particular issue. Solved by How to conditionally filter IQueryable
private static readonly PropertyInfo _OrganisationIDProperty = ReflectionAPI.GetProperty<OrganisationDependent, int>(o => o.OrganisationID);
private static Expression<Func<TOrg, bool>> FilterByOrganization<TOrg>(int organizationId)
{
//The FilterByOrganisation method uses the LINQ Expressions API to generate an expression that will filter on organisation id
//This avoids having to cast the set using .AsEnumerable().Cast<OrganisationDependent>().Where(x => x.OrganisationID == CurrentOrganisationID).AsQueryable().Cast<T>();
//https://stackoverflow.com/questions/20052827/how-to-conditionally-filter-iqueryable-by-type-using-generic-repository-pattern
var item = Expression.Parameter(typeof(TOrg), "item");
var propertyValue = Expression.Property(item, _OrganisationIDProperty);
var body = Expression.Equal(propertyValue, Expression.Constant(organizationId));
return Expression.Lambda<Func<TOrg, bool>>(body, item);
}
public virtual IQueryable<T> All
{
get
{
if (typeof(T).IsSubclassOf(typeof(OrganisationDependent)))
return Context.Set<T>().Where(FilterByOrganization<T>(CurrentOrganisationID));
return Context.Set<T>();
}
}
This closes off most of the places that a user could access someone else's data. But it doesn't filter navigational properties. So I have to add code to all navigation properties on non-organisation dependent entities to do that.
I don't want to separate my data into different database, but one day I will find out if it's feasible to create views filtered by organisation in different schemas - with the same name and structure as my tables, then switch schema according to user.....oh and I want to automatically create them for each new organisation and autmatically migrate them using code-first too....
And you could vote to Allow filtering for Include extension method here
If you're using a CQRS style architecture you can think about having one or more viewmodels per user that contains the products/customers that they have access to.
If you see yourself having to implement logic on the query side of CQRS that is a strong indication that you are doing something wrong.
I have a User entity that has a IList property. The mapping for
this looks like this:
HasMany(x => x.Forms)
.Cascade.None()
.AsBag().KeyColumn("Supervisor")
.Key(key => key.Not.Update())
.PropertyRef("Email");
Now I have a new feature request in that essentially adds another
KeyColumn that should ALSO populate into this property. Essentially,
each form can have a "Supervisor" and an "Alternate", and I need this
property to populate any Forms where the User.Email is equal to either
one (Forms BELONG to a User IF the User.Email == Form.Supervisor OR User.Email == Form.Alternate).
Is there a way to do this? Simply adding another KeyColumn
specification simply overrides the previous one, and adding keys via
the Keys() method seems to not allow for multiple key columns like I
want... I'm looking for a way to essentially tell it that this
relationship should be found with a WHERE Superviser = Email OR Alternate = Email, but it doesn't seem to support that OR clause...
My other option is essentially duplicate this mapping, but for "Alternate" in another property, and then slam the collections together in my consuming code, but I wanted to see if NHibernate was smart enough for this to be possible...
Any suggestions?
To my knowledge, no - NHibernate does not support this unusual type of join. I would approach this exactly like you said. Use two collections - User.SupervisorForms and User.AlternateForms, then combine them later, perhaps using LINQ's Concat method to do so. You could define a property to do this concatenation for you:
public virtual IEnumerable<Form> Forms
{
get { return SupervisorForms.Concat(AlternateForms); }
}
If you wish to load a User with a completely initialized Forms collection in a single round-trip to the database, then you can use this approach from the Improving Performace - Multi Query section of the NHibernate documentation:
User user = s.CreateMultiQuery()
.Add("select u from User u left join fetch u.SupervisorForms where u.Id = :id")
.Add("select u from User u left join fetch u.AlternateForms where u.Id = :id")
.SetInt32("id", 123)
.UniqueResult<User>();
This code uses CreateMultiQuery() and HQL - but the approach should work just as well with your choice of query batching syntax: CreateMultiQuery(), CreateMultiCriteria(), or Future() / FutureValue().
I've been working around a problem I have when using LINQ to Entities when using closures.
Apparently, L2E does not support closures. Meaning:
var users = from user in UserRepository.FindAll()
select new UserDTO
{
UserId = user.UserId,
Tickets = from ticket in TicketRepository.FindAll()
where ticket.User == user
select new TicketDTO
{
TicketId = ticket.TicketId
}
};
(NOTE: The "where"-clause is where the problem exists. I am not allowed to compare an entity to another entity because they are not EF primitive types. Only things like Int32, Guid etc. is allowed.)
, is not valid because I cannot compare 'ticket.User' to 'user'
This is simply an example of the problem I have, and I realize that I could compare on the Id, since this a primitive type, as opposed to a closure.
In reality my scenario is alot more complex than this, but this is the scenario I need to solve for now.
A work-around I found online is using a subquery. That DOES work, but for my scenario it's not very effective.
Question:
Do any of you know if:
Entity Framework 4 will support Closures in LINQ to Entities?
There is a better solution to this problem than using sub-queries?
Any additional knowledge you have on this topic will be greatly appreciated!
This is not a problem directly related to closures. The problem is (probably) that you are mixing Entity Framework entities and your data transfer objects. The LINQ provider tries to convert the expression tree of your query into SQL statements and fails because it cannot separate the data transfer objects from the entities and the database, of course, cannot deal with the data transfer objects, too.
I suggest to make the separation much cleaner - at first fetch the data from the database using LINQ to Entity and maybe anonymous types if required, then switch to LINQ to Objects to construct data transfer objects from the retrieved data and all should be fine. Something like the following. (Just to note - I am (safely) assuming the repositories return IQueryable<T>s (else the whole stuff should not work at all).)
var result = UserRepository
.FindAll()
.Select(user => new
{
UserId = user.UserId,
TicketIds = TicketRepository
.FindAll()
.Where(ticket => ticket.User.UserId == user.UserId)
.Select(ticket => ticket.TicketId)
});
Transforming this query result into data transfer objects is now straight forward. Note that the users are compared via the IDs because the Entity Framework does (not yet) support comparisons by reference.
The problem here is that L2E doesn't support reference equality of materialized objects vs. objects in the DB, so you need to compare based on the PK:
var users = from user in UserRepository.FindAll()
select new UserDTO
{
UserId = user.UserId,
Tickets = from ticket in TicketRepository.FindAll()
where ticket.User.UserId == user.UserId
select new TicketDTO
{
TicketId = ticket.TicketId
}
};
(Presuming, here, that the PK of User is called UserId.)