Can I use Entity Framework to save changes to a view?
I have an entity which is mapped to a View.
[Table("MyView")]
public class MyEntity
{
public long MyEntityId { get; set; }
public string Name { get; set; }
}
The View itself is like this:
CREATE VIEW MyView AS
SELECT
t.MyEntityId,
t.Name,
FROM
MyTable t
Would I be able to use Entity Framework change tracking to save changes to this View? So is something like this possible:
var record = Context.MyEntity.Where(e => e.MyEntityId == 150).FirstOrDefault();
record.Name = "New Name";
Context.SaveChanges();
Looks like Entity Framework does not care if the Entity is mapped to a View or Table... it would just create the same update script. For the example above EF generates the following script:
UPDATE [MyView] SET [Name]=#gp1 WHERE [MyEntityId] = 150
-- #gp1: 'New Name' (Type = String, IsNullable = false, Size = 8)
So EF does not introduce any additional limitation for updating a View... but we still have the RDBMS specific limitations for updating a View... as an example, in SQL Server a view can be updated subject to the following limitations:
If the view contains joins between multiple tables, you can only insert and update one table in the view, and you can't delete rows.
You can't directly modify data in views based on union queries. You can't modify data in views that use GROUP BY or DISTINCT statements.
All columns being modified are subject to the same restrictions as if the statements were being executed directly against the base
table.
Text and image columns can't be modified through views.
There is no checking of view criteria. For example, if the view selects all customers who live in Paris, and data is modified to
either add or edit a row that does not have City = 'Paris', the data
will be modified in the base table but not shown in the view, unless
WITH CHECK OPTION is used when defining the view.
Related
I am working on a .NET Core 3.1 Web API. Suppose I have the following model classes:
This is a model that points to an underlying database table called TableA:
[Table("TableA")]
public class TableA
{
public string ColumnA { get; set; }
}
This is a model that inherits from TableA but gets extra columns from a view in the database:
[Table("ViewTableA")]
public class ViewTableA : TableA
{
public string ColumnA { get; set; }
other properties from view ...
}
When reading from the database I will query using the ViewTableA model. Since the ViewTableA has the attribute [Table("ViewTableA")] entity framework with generate the query to pull data from the ViewTableA view.
My question is if I had the following:
ViewTableA objA = new ViewTableA(){ populate properties here};
TableA objB = (TableA)objA;
If I were to add objB to the DBContext and save the changes which table/view would it hit? The TableA table since objB is of type TableA which has an attribute of [Table("TableA")] or would it still hit the view called ViewTableA?
objB won't be change tracked until you call db.Set<T>().Add(objB); If you call db.Set<ViewTableA>().Add(objB), all is well. If you call db.Set<TableA>().Add(objB) EF might insert into TableA or ViewTableA, or fail. I'm not sure which, and you shouldn't rely on the behavior.
Suppose the following tables
ParentEntities
ParentID
ChildEntities
ChildID
ParentID
These tables do not have a FK defined in the schema.
In EF designer, after generating from DB, I add an association:
- Parent Multiplicity: 1
- Child Multiplicity: 0 or 1
When I build, I get the error: "Error 3027: No mapping specified for the following EntitySet/AssociationSet - ParentChild"
But if I try to configure table mapping for the association like this..
Maps to ChildEntities
Parent
ParentID <-> ParentID (parent entity prop <-> child table column)
Child
ChildID <-> ChildID (child entity prop <-> child table column)
.. I get this: Error 3007: Problem in mapping fragments starting at lines xxx, xxx: Column(s) [ParentID] are being mapped in both fragments to different conceptual side properties.
Why this is an error doesn't make sense. Limitation of the current implementation?
[EDIT 1]
I'm able to make this work by creating a 1-N association. That's not ideal, but it works just the same, just have to add a read-only child property in a partial:
public partial class Parent
{
public Child Child { get { return Childs.Any() ? null : Childs.First(); } }
}
This seems like the best solution for me. I had to add a FK to the database to get EF to generate the association and navigation property, but once it was added I was able to remove the FK, and further updates to the model from the DB did not remove the association or Navigation properties.
[EDIT 2]
As I was investigating how to work around not caring about the association being modeled in EF, I ran into another issue. Instead of the read-only Child property I made it normal ..
public partial class Parent
{
public Child Child { get; set; }
}
.. but now I need a way to materialize that from the query:
var query = from parents in context.Parents
// pointless join given select
join child in context.Childs
on parents.ParentID equals child.ParentID
select parents;
I can select an anonymous type ..
// step 1
var query = from parents in context.Parents
join child in context.Childs
on parents.ParentID equals child.ParentID
select new { Parent = parents, Child = child };
.. but then I've got to consume more cycles getting that into my entity:
// step 2
var results = query.Select(x => {
var parent = x.Parent;
parent.Child = x.Child;
return parent; });
Is there a better/streamlined way to do this from the query select so the EF materializer can do it from the get-go? If not, then I'll resort to Edit 1 methodology ..
Ef Code first requires 1->0..1 relationships for the Child to have the same primary key.
Maybe this a similar restriction In the modeler in this circumstance.
ParentId (Key) required in Both tables.
I have never tried adding such relationships in designer afterwords in DB first.
EDIT: to match your EDIT2:
I would stay on the direction . Use Navigation properties to get from Join back to original class A and B.
query = context.Set<JoinTable>.Where(Jt=>Jt.NavA.Id == ClassAId
&& Jt.navB.Id == ClassBId)
use a select if your need entries returned from either ClassA or ClassB.
I have following structure in Oracle database:
Course(CourseId, Name)
->Student(StudentId, Name, Comment, CourseId)
->Subject(SubjectId, Name, SubjectComment, CourseId)
Student contains some of Primitive Properties (StudentId, Name, CourseId, Comment) and Navigation Property (Courses [Linked with DTO name Course on CourseId]).
Table structure is also same as Entity structure and currenly using Explicit loading to extract the data from Oracle database, using LoadProperty and Load.
I need to load the Collection and Object with selected property, as Load Student with StudentId and Name (without Comment column).
LoadProperty(Student, s => s.Courses), load only CourseId (don't load Name primitive property in Course DTO). So, Student.Courses.First().CourseId will be a value and Name will be null as intentionally excluded from loading from database.
LoadProperty(Course, c => c.Subjects) load only SubjectId without Name property, even don't go to database to load.
Is there any way to Include/Exclude the Primitive types to load?
Not with load property and not with entities. You must create custom query and use projection to load only selected columns:
var student = from s in context.Students
select new {
StudentId = s.StudentId,
... // Other student's properties
CourseId = s.Course.Id,
SubjectIds = s.Courese.Subjects.Select(s => s.Ids)
};
Say I have two entities with about 20 properties per entity and a Many-to-Many relationship like so:
User (Id int,Name string, .......)
Issue (Id int,Name string, .......)
IssueAssignment (UserId,RoleId)
I want to create a new Issue and assign it to a number of existing Users. If I have code like so:
foreach(var userId in existingUserIds)
{
int id = userId
var user = _db.Users.First(r => r.Id == id);
issue.AssignedUsers.add(user);
}
_db.Users.AddObject(user);
_db.SaveChanges();
I noticed it seems terrribly inefficient when I run it against my SQL Database. If I look at
the SQL Profiler it's doing the following:
SELECT TOP(1) * FROM User WHERE UserId = userId
SELECT * FROM IssueAssignment ON User.Id = userId
INSERT INTO User ....
INSERT INTO IssueAssignment
My questions are:
(a) why do (1) and (2) have to happen at all?
(b) Both (1) and (2) bring back all fields do I need to do a object projection to limit the
fields, seems like unnecessary work too.
Thanks for the help
I have some possible clues for you:
This is how EF behaves. _db.Users is actaully a query and calling First on the query means executing the query in database.
I guess you are using EFv4 with T4 template and lazy loading is turned on. T4 templates create 'clever' objects which are able to fixup their navigation properties so once you add a User to an Issue it internally triggers fixup and tries to add the Issue to the User as well. This in turns triggers lazy loading of all issues related to the user.
So the trick is using dummy objects instead of real user. You know the id and you only want to create realtion between new issue and existing user. Try this (works with EFv4+ and POCOs):
foreach(var userId in existingUserIds)
{
var user = new User { Id = userId };
var _db.Users.Attach(user); // User with this Id mustn't be already loaded
issue.AssignedUsers.Add(user);
}
context.Issues.AddObject(issue);
context.SaveChanges();
I'm trying to put together a ViewModel that will have a list of users and each user will have a list of locations.
The User table and Location table are joined together through another table that holds each respective ID and some other information. This table is essentially a many to many join table.
I've tried a few different viewModel approaches and they we're severely lacking... What would be the best approach for displaying this type of information?
I assume that the issue is that you want to be able to access the collection by either User or Location. One approach could be to use ILookup<> classes. You'd start with the many-to-many collection and produce the lookups like this:
var lookupByUser = userLocations.ToLookup(ul => ul.User);
var lookupByLocation = userLocations.ToLookup(ul => ul.Location);
Update:
Per your description, it seems like you don't really need to have a full many-to-many relationship in your ViewModel. Rather, your ViewModel could have a structure like this:
public class YourViewModel
{
public IEnumerable<UserViewModel> Users { get; set; }
}
public class UserViewModel
{
// User-related stuff
public IEnumerable<LocationViewModel> Locations { get; set; }
}
If you wanted to avoid redundant LocationViewModel objects, you could pre-build a mapping between your Model and ViewModel objects:
var locationViewModels = myLocations.ToDictionary(
loc => loc, loc => CreateLocationViewModel(loc));
And then reuse these objects when populating your page's ViewModel.