This may not be the right approach however, perhaps someone will have a better take on it
I am looking for a way to force a parameter query when calling a DBSet property in a DBContext.
public DbSet<User> Users { get { return(User.Where(x=>x.TenantId == TenantId))} set; }
Related
With its recent improvements, I'm looking to move from Dapper back to EF (Core).
The majority of our code currently uses the standard patterns of mapping entities to tables, however we'd also like to be able to make simple ad-hoc queries that map to a simple POCO.
For example, say I have a SQL statement which returns a result set of strings. I created a class as follows...
public class SimpleStringDTO
{
public string Result { get; set; }
}
.. and called it as such.
public DbSet<SimpleStringDTO> SingleStringResults { get; set; }
public IQueryable<SimpleStringDTO> Names()
{
var sql = $"select name [result] from names";
var result = this.SingleStringResults.FromSql(sql);
return result;
}
My thoughts are that I could use the same DBSet and POCO for other simple queries to other tables.
When I execute it, EF throws an error "The entity type 'SimpleStringDTO' requires a primary key to be defined.".
Do I really need to define another field as a PK? There'll be cases where there isn't a PK defined. I just want something simple and flexible. Ideally, I'd rather not define a DBSet or POCO at all, just return the results straight to an IEnumerable<string>.
Can someone please point me towards best practises here?
While I wait for EF Core 2.1 I've ended up adding a fake key to my model
[Key]
public Guid Id { get; set; }
and then returning a fake Guid from SQL.
var sql = $"select newid(), name [result] from names";
So with entity framework I'm trying to update two existing entities.
There I've the main object something like:
public class MainObject
{
public string Name { get; set; }
public virtual SmallObject Part { get; set;}
}
public class SmallObject
{
public string Name { get; set; }
}
In the repository I first check if the SmallObject already exists in the database by:
MainObject.Part = (from s in repoSmallObject.GetAll()
where s.name == MainObject.Part.Name
select s).FirstOrDefault();
Then finally I call the update method in my GenericRepository
repoMainObject.Update(MainObject)
which is defined as a generic repository method:
dbSet.Attach(entity)
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
But the relationship doesn't get updated. Why is that? Both objects are attached to context not?
*Edit: The two repo's are injected with the same Context.
And strangely enough the Add method works and also updates the relationship.
When you set
context.Entry(entity).State = EntityState.Modified;
you need at least to set the state after and before updates (i.e. context.Entry(mainObject).CurrentValues and OriginalValues) so EF can build the right UPDATE query (with right WHERE clause).
It works if you set
context.Entry(entity).State = EntityState.Added;
because EF needs just to generate an INSERT query.
I don't know exactly why you need it but usually I prefer to attach the object to the DbSet and modify the properties so EF handles various states.
dbSet.Attach(MainObject)
MainObject.Part = (from s in repoSmallObject.GetAll()
where s.name == MainObject.Part.Name
select s).FirstOrDefault();
(In your case does not work because MainObject.Part.Name does not change)
The attached object should have the same values of the database otherwise you have a concurrency exception.
BTW, why you don't read the old object (MainObject) from the DB than work on it???
I want to intercept all database query to add filters based on authorization info.
I implement the IDbCommandTreeInterceptor interface, but i don't known how to modify the tree query.
For example, i have the ClassA:
public class ClassA {
public int Id { get; set; }
public string Name { get; set; }
}
And now, i try to intercept all querys to filter all id's < 2
interceptor.Result.Where(classa = classa.Id < 2)
Is my idea correct? Is the IDbCommandTreeInterceptor interface the proper way to do this?
I don't found information about this interface in Internet.
Thanks
See the example that EF team member Rowan Miller presented at TechEd 2014. His example was SoftDeleteInterceptor (on GitHub) that changes a DELETE to an UPDATE of an IsDeleted flag on the entity.
I'll try and be as descriptive as I can in this post. I've read a dozen or more SO questions that were peripherally related to my issue, but so far none have matched up with what's going on.
So, for performing audit-logging on our database transactions (create, update, delete), our design uses an IAuditable interface, like so:
public interface IAuditable
{
Guid AuditTargetId { get; set; }
int? ContextId1 { get; }
int? ContextId2 { get; }
int? ContextId3 { get; }
}
The three contextual IDs are related to how the domain model is laid out, and as noted, some or all of them may be null, depending on the entity being audited (they're used for filtering purposes for when admins only want to see the audit logs for a specific scope of the application). Any model that needs to be audited upon a CUD action just needs to implement this interface.
The way that the audit tables themselves are being populated is through an AuditableContext that sits between the base DbContext and our domain's context. It contains the audit table DbSets, and it overrides the SaveChanges method using the EF ChangeTracker, like so:
foreach (var entry in ChangeTracker.Entries<IAuditable>())
{
if (entry.State != EntityState.Modified &&
entry.State != EntityState.Added &&
entry.State != EntityState.Deleted)
{
continue;
}
// Otherwise, make audit records!
}
base.SaveChanges();
The "make audit records" process is a slightly-complex bit of code using reflection and other fun things to extract out fields that need to be audited (there are ways for auditable models to have some of their fields "opt out" of auditing) and all that.
So that logic is all well and good. The issues comes when I have an auditable model like this:
public class Foo: Model, IAuditable
{
public int FooId { get; set; }
// other fields, blah blah blah...
public virtual Bar Bar { get; set; }
#region IAuditable members
// most of the auditable members are just pulling from the right fields
public int? ContextId3
{
get { return Bar.BarId; }
}
#endregion
}
As is pointed out, for the most part, those contextual audit fields are just standard properties from the models. But there are some cases, like here, where the context id needs to be pulled from a virtual complex property.
This ends up resulting in a NullReferenceException when trying to get that property out from within the SaveChanges() method - it says that the virtual Bar property does not exist. I've read some about how ChangeTracker is built to allow lazy-loading of complex properties, but I can't find the syntax to get it right. The fields don't exist in the "original values" list, and the object state manager doesn't have those fields, I guess because they come from the interface and not the entities directly being audited.
So does anyone know how to get around this weird issue? Can I just force eager-loading of the entire object, virtual properties included, instead of the lazy loading that is apparently being stubborn?
Sorry for the long-ish post, I feel like this is a really specific problem and the detail is probably needed.
TIA! :)
I'm using VS2010, EF4 feature CTP (latest release), and POCO objects, such as the example below:
class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual IList<Account> Accounts { get; set; }
...
}
class Account
{
public string Number { get; set; }
public int ID { get; set; }
...
}
For the sake of brevity, assume context below is the context object for EF4. I have a dbml mapping between entity types and the database, and I use it like this with no problem:
Person doug = context.Persons.CreateObject();
doug.Name = "Doug";
context.Add(doug);
context.Save();
doug.Accounts.Add(new Account() { Name = "foo" });
context.Save(); // two calls needed, yuck
At this point, the database has a Person record with the name "Doug", and an account record "foo". I can query and get those record back just fine. But if I instead try to add the account before I save the Person, the Accounts list is null (the proxy hasn't created an instance on that property yet). See the next example:
Person doug = context.Persons.CreateObject();
doug.Name = "Doug";
doug.Accounts.Add(new Account() { Name = "foo" }); // throws null reference exception
context.Add(doug);
context.Save();
Has anybody else encountered this? Even better, has anyone found a good solution?
Person doug = context.Persons.CreateObject();
doug.Name = "Doug";
context.Add(doug);
doug.Accounts.Add(new Account() { Name = "foo" });
context.Save();
This will work
Yes and yes!
When you new the POCO up (as opposed to CreateObject from the Context), no proxies are provided for you. This may seem obvious, but I had to explicitly remind myself of this behavior when chasing a similar issue down. (I know this isn't the situation you described in the question, but the overall issue should be acknowledged).
Initializing collections in the constructor of the POCO does not interfere with proper EF4 proxy lazy-loading behavior, from what I've observed in my own testing.
OK, all this being said, I now see your comment to the previous answer -- why don't I have a proxied Addresses collection when I request a new Person from my context? Do you have lazy loading enabled on the context? Seeing how we're dealing with navigation properties, I could see where having lazy loading turned off may make a difference in this situation.
ISTM that if you expect the framework to do all this for you then you wouldn't really have a "POCO", would you? Take your Person class, with the code above. What would you expect the state of the Accounts property to be after construction, with no constructor, if the EF weren't involved? Seems to me that the CLR will guarantee them to be null.
Yes, proxies can initialize this when necessary for materialization of DB values, but in the EF, "POCO" actually means "Plain". Not "something packed with runtime-generated code which we pretend is 'Plain'".