Apply EF global query filter in an async way - entity-framework

I need to apply a global query filter in my EF DbContext.
The value to check against is retrieved by an async operation.
So something like this.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().HasQueryFilter(async u => u.CustomerId == await GetIdAsync());
}
Of course this doesn't work, as HasQueryFilter expects a different lamda expression.
And I guess, that adding async to the OnModelCreating will bring me into troubles, too.
protected override async void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var id = await GetIdAsync();
modelBuilder.Entity<User>().HasQueryFilter(u => u.CustomerId == id);
}
Any ideas, how to solve this?

Define property CustomerId in your DbContext:
public class MyDbContext: DbContext
{
public int? CustomerId { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().HasQueryFilter(u => CustomerId == null || u.CustomerId == CustomerId);
}
}
Assign property in controller call:
context.CustomerId = await context.GetIdAsync();
And then you can sefely use Query Filter.

Related

Filter name MustHaveTenant not found

I use ABP, and I am getting an error:
Filter name MustHaveTenant not found
I don't need multiple tenants.
I checked the ABP document, and I did use this code:
Configuration.UnitOfWork.OverrideFilter(AbpDataFilters.MustHaveTenant, false);
But I still get the error.
DbContext code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AuditLog>().Property(t => t.RequestId).HasMaxLength(50);
modelBuilder.Entity<AuditLog>().Property(t => t.Method).HasMaxLength(20);
}
First if you don't need to use MiltiTenancy you can turn it off on your ProyectCoreModule
Configuration.MultiTenancy.IsEnabled = PuntoVentaConsts.MultiTenancyEnabled;
In this line you can change it to false or change the const property value to false
And you're getting the filter error because you omitted the call to
base.OnModelCreating(modelBuilder); in your OnModelCreating() method, it has to be like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AuditLog>().Property(t => t.RequestId).HasMaxLength(50);
modelBuilder.Entity<AuditLog>().Property(t => t.Method).HasMaxLength(20);
}

Entity Framework CodeFirst, Add Dbset to DbContext, programmatically

how can i Add DbSet to my dbContext class, programmatically.
[
public class MyDBContext : DbContext
{
public MyDBContext() : base("MyCon")
{
Database.SetInitializer<MyDBContext>(new CreateDatabaseIfNotExists<MyDBContext>());
}
//Do this part programatically:
public DbSet<Admin> Admins { get; set; }
public DbSet<MyXosh> MyProperty { get; set; }
}
][1]
i want to add my model classes by ((C# Code-DOM)) and of course i did. but now i have problem with creating DbSet properties inside my Context class ...
yes i did!..
this: https://romiller.com/2012/03/26/dynamically-building-a-model-with-code-first/
And this: Create Table, Run Time using entity framework Code-First
are solution. no need to dispute with dbSets directly. it just works by do some thing like that:
public class MyDBContext : DbContext
{
public MyDBContext() : base("MyCon")
{
Database.SetInitializer<MyDBContext>(new CreateDatabaseIfNotExists<MyDBContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
var theList = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.Namespace == "FullDynamicWepApp.Data.Domins")
.ToList();
foreach (var item in theList)
{
entityMethod.MakeGenericMethod(item)
.Invoke(modelBuilder, new object[] { });
}
base.OnModelCreating(modelBuilder);
}
}
For those using EF Core that stubble here:
The code below is only for one table with the generic type. If you want more types you can always pass them through the constructor and run a cycle.
public class TableContextGeneric<T> : DbContext where T : class
{
private readonly string _connectionString;
//public virtual DbSet<T> table { get; set; }
public TableContextGeneric(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var entityMethod = typeof(ModelBuilder).GetMethods().First(e => e.Name == "Entity");
//the cycle will be run here
entityMethod?.MakeGenericMethod(typeof(T))
.Invoke(modelBuilder, new object[] { });
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString); // can be anyone
}
}

Entity Framework table not updating database

My application does something strange, if I execute the following code outside the debugger it does not work, but if I run it with the debugger it works fine: keeping in mind that I step through the code, and not continue the next instant.
As I can gather from the debugger, there is nothing wrong with the code, but maybe there is, I at least cannot find it.
public void Reject(string id)
{
using (context)
{
int intId = Convert.ToInt32(id);
Hours hr = (from h in context.Hours
where h.Id == intId
select h).FirstOrDefault();
hr.Status = 30;
context.SaveChanges();
}
}
ApplicationDBContext class
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
static ApplicationDbContext()
{
System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
}
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<Department> Departments { get; set; }
public DbSet<Tasks> Tasks { get; set; }
public DbSet<Hours> Hours { get; set; }
public DbSet<OffDays> OffDays { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasRequired(u => u.Department)
.WithMany(d => d.Users)
.Map(c => c.MapKey("DepartmentId"));
modelBuilder.Entity<ApplicationUser>()
.HasMany(u => u.Supervisors)
.WithMany();
modelBuilder.Entity<Department>()
.HasMany(d => d.Supervisors)
.WithOptional()
.Map(c => c.MapKey("SupervisorId"));
modelBuilder.Entity<Hours>()
.HasRequired(u => u.UserId)
.WithMany(h => h.Hours)
.Map((c => c.MapKey("Hours")));
}
}
Do you guys have an idea what I can try?
It looks like you are reusing the same DbContext instance through the life of your application. This goes against Microsoft's recommended best practices, and may introduce some of the odd behavior you are seeing. DbContext manages a LOT of cached data inside each DbContext instance, and keeping it alive for the entire application can cause all sort of craziness as different cached entities are returned. This caching is what I think is causing your problem. Somewhere in your application this instance is getting disconnected from the context, and now that disconnected, cached instance is being returned the next time you ask for it.
The current Entity Framework best practices recommend that you create a new instance of the context for each logical "unit of work".
The easiest way to create a new context instance on demand is like this:
using (var context = new ApplicationDbContext())
{
//Code here that uses the context
}
You must first attach the entity that you have retrieved
public void Reject(string id)
{
using (context)
{
int intId = Convert.ToInt32(id);
Hours hr = (from h in context.Hours
where h.Id == intId
select h).FirstOrDefault();
context.Attach( hr);// here is difference
hr.Status = 30;
context.SaveChanges();
}
}

Breeze EF SaveChanges() on a DTO

I've been struggling with Breeze to SaveChanges to a projection and admit I new to both EF and breeze. There were some similar questions earlier when I was trying to use WCF, but now I have abandoned WCF and added EF directly to my solution.
In my controller I return the DTO for the metadata to breeze along with the DTO and it binds perfectly.
After altering the data on the client my Breese Controllers [HttpPost] SaveChanges(save Bundle) is called and map contains the DTO and the changes.
How Do I persist the Changes? If I re-read the DTO projection for breeze to update then EF cant save a projection because it's not "tracked", if I read the Full entity, then Breeze error with "Sequence contains no matching element" because its looking for the DTO? Am I suppose to use AutoMapper?
Controller:
[BreezeController]
public class BreezeController : ApiController
{
readonly EFContextProvider<ManiDbContext> _contextProvider = new EFContextProvider<ManiDbContext>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpGet]
public IQueryable<ConsigneDTO> Consignee(string refname)
{
return _contextProvider.Context.consigneDTO(refname);
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
ManiDbContextProvider _mcontextProvider = new ManiDbContextProvider();
return _mcontextProvider.SaveChanges(saveBundle);
}
ManiDbContext (the main DBContext is CifContext which is Database First/EF Reverse Engineer)
public class ManiDbContext : DbContext
{
public CifContext CifDbContext = new CifContext();
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
Database.SetInitializer<ManiDbContext>(null);
modelBuilder.Configurations.Add(new ConsigneDTOMap());
}
public override int SaveChanges()
{
CifDbContext.SaveChanges();
return 1;
}
public IQueryable<ConsigneDTO> consigneDTO(string refname)
{
IQueryable<ConsigneDTO> q = this.CifDbContext.Consignes
.Where(x => x.Refname == refname)
.Select(f => new ConsigneDTO {Refname = f.Refname, Consignee = f.Consignee, Address1 = f.Address1, Address2 = f.Address2, Address3 = f.Address3});
return q;
}
ManiDbContextProvider
public class ManiDbContextProvider : EFContextProvider<CifContext>
// public class ManiDbContextProvider : EFContextProvider<ManiDbContext>
{
public ManiDbContextProvider() : base() { }
protected override void OpenDbConnection()
{// do nothing
}
protected override void CloseDbConnection()
{ // do nothing
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
var entity = entityInfo.Entity;
if (entity is ConsigneDTO)
{
return BeforeSaveConsignee(entity as ConsigneDTO, entityInfo);
}
throw new InvalidOperationException("Cannot save entity of unknown type");
}
private bool BeforeSaveConsignee(ConsigneDTO c, EntityInfo info)
{
var consdata = this.Context.CifDbContext.Consignes
.Where(x => x.Refname == c.Refname)
.FirstOrDefault(); // ENTITY
// var consdata = this.Context.consigneDTO(c.Refname); // DTO
return (null != consdata) || throwCannotFindConsignee();
}
CifContext (Full Columns - First/EF Reverse Engineer/ Consigne class contains Keys)
public partial class CifContext : DbContext
{
static CifContext()
{
Database.SetInitializer<CifContext>(null);
}
public CifContext()
: base("Name=CifContext")
{
}
public DbSet<Consigne> Consignes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); // Use singular table names
Database.SetInitializer<CifContext>(null);
modelBuilder.Configurations.Add(new ConsigneMap());
}
Regardless if I Read the Entity or the DTO - I'm Clueless on how breeze updates EF
Any Help greatly appreciated :)
Regards,
Mike

EF 4.1 RTM - EntityTypeConfiguration

I have been using EF Code First CTP5 with dedicated mapping classes for each entity, like this:
public class UserMapping : EntityTypeConfiguration<User>
{
public UserMapping()
{
ToTable("Users");
HasKey(t => t.ID);
Property(t => t.ID).HasColumnName("user_id");
Property(t => t.Name).HasColumnName("name");
}
}
And loading them the way Jonas Cannehag describes here:
http://areaofinterest.wordpress.com/2010/12/08/dynamically-load-entity-configurations-in-ef-codefirst-ctp5/
But that doesn't work in RTM and I haven't been able to figure out how to use dedicated mapping classes. Have you? :-)
public class DataContext : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserMapping());
base.OnModelCreating(modelBuilder);
}
}
dynamic version (probably should test extensive before putting in production)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister =
Assembly.GetExecutingAssembly().GetTypes().Where(
type =>
type.BaseType.IsGenericType &&
type.BaseType.GetGenericTypeDefinition() == typeof (EntityTypeConfiguration<>));
foreach (object configurationInstance in typesToRegister.Select(Activator.CreateInstance))
{
modelBuilder.Configurations.Add((dynamic) configurationInstance);
}
base.OnModelCreating(modelBuilder);
}