Hello I have a stored procedure called dbo.GetFolderDocumentsHierarchy
and it maps directly to the following class.
public class DocumentDto:IModel
{
public int FolderId { get; set; }
[CanBeNull]
public int Id { get; set; }
public string FolderName { get; set; }
public string DocumentName { get; set; }
public string ParentName { get; set; }
public int? ParentId { get; set; }
}
I have added it to my Context like so.
builder.Query<DocumentDto>();
I might add that I call it like:
var dtos = await _context
.Set<DocumentDto>()
.FromSqlRaw("EXEC dbo.GetFolderDocumentsHierarchy @Skip, @Take, @UserId", sqlParameters)
.ToListAsync();
Now on to my issue, every time I create a new migration it adds the following code to the migration and to the snapshot.
migrationBuilder.CreateTable(
name: "DocumentDto",
columns: table => new
{
FolderId = table.Column<int>(nullable: false),
Id = table.Column<int>(nullable: false),
FolderName = table.Column<string>(nullable: true),
DocumentName = table.Column<string>(nullable: true),
ParentName = table.Column<string>(nullable: true),
ParentId = table.Column<int>(nullable: true)
},
constraints: table =>
{
});
And in the snapshot:
modelBuilder.Entity("Models.Dtos.DocumentDto", b =>
{
b.Property<string>("DocumentName")
.HasColumnType("nvarchar(max)");
b.Property<int>("FolderId")
.HasColumnType("int");
b.Property<string>("FolderName")
.HasColumnType("nvarchar(max)");
b.Property<int>("Id")
.HasColumnType("int");
b.Property<int?>("ParentId")
.HasColumnType("int");
b.Property<string>("ParentName")
.HasColumnType("nvarchar(max)");
b.ToTable("DocumentDto");
});
How can I make this not add to the migrations anymore?
EDIT: Adding full context.
public virtual DbSet<ApplicationUser> ApplicationUsers { get; set; }
public virtual DbSet<Appointment> Appointments { get; set; }
public virtual DbSet<Campaign> Campaigns { get; set; }
public virtual DbSet<Contact> Contacts { get; set; }
public virtual DbSet<Customer> Customers { get; set; }
public virtual DbSet<Deal> Deals { get; set; }
public virtual DbSet<Document> Documents { get; set; }
public virtual DbSet<Folder> Folders { get; set; }
public virtual DbSet<Group> Groups { get; set; }
public virtual DbSet<GroupDocument> GroupDocuments { get; set; }
public virtual DbSet<UserDocument> UserDocuments { get; set; }
public virtual DbSet<UserGroup> UserGroups { get; set; }
public virtual DbSet<UserFolder> UserFolders { get; set; }
public virtual DbSet<UserTask> UserTasks { get; set; }
public virtual DbSet<Opportunity> Opportunities { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<UserTask>()
.HasIndex(x => x.OwnerId);
builder.Entity<Group>()
.HasMany(x => x.Campaigns)
.WithOne(x => x.Group);
#pragma warning disable 618 builder.Query(); #pragma warning restore 618
builder.Entity<GroupDocument>()
.HasKey(gd => new { gd.GroupId, gd.DocumentId });
builder.Entity<UserDocument>()
.HasKey(x => new {x.UserId, x.DocumentId});
builder.Entity<UserFolder>()
.HasKey(x => new {x.UserId, x.FolderId});
}
The main trick is mentioned in Ivan Stoev's answer here , which is to add a ToView
mapping statement, always. Even when there is no view. The only difference is that EF core 5 doesn't support .ToView(null)
, you have to enter some string.
In general, your options are:
1
modelBuilder.Entity<DocumentDto>
( eb =>
{
eb.HasNoKey();
eb.ToSqlQuery("EXEC StoredProcedureWithoutParameters");
// Or: eb.ToSqlQuery("SELECT ... FROM ... ");
eb.ToView("dummy view name"); // To prevent table creation.
}
);
Or, when there is a view you map the DTO to:
2
modelBuilder.Entity<DocumentDto>
( eb =>
{
eb.HasNoKey();
eb.ToView("MyView"); // Map to existing view
}
);
Or, just to map the DTO as keyless entity:
3
modelBuilder.Entity<DocumentDto>
( eb =>
{
eb.HasNoKey();
eb.ToView("Dummy view name"); // To prevent table creation
}
);
When using option 3, you should populate the DTOs by code that you already have, something like:
context.Set<DocumentDto>()
.FromSqlRaw("EXEC dbo.GetFolderDocumentsHierarchy @Skip, @Take, @UserId",
sqlParameters)
This is the only way, to my knowledge, to use a stored procedure with parameters. Maybe there is some fancy way to use option 1 and make EF pass the predicates of a Where
statement to the raw SQL query, but I doubt it.
I think the best option is to map the DTO to a real view (option 2, which you do now, as you mention in a comment), because in that case the IQueryable
can be filtered by a Where
statement. Also, it is composable in a way that generates one SQL statement, which allows the database engines query optimizer to calculate an overall query plan. Of course, this is only possible if the stored-procedure code can be transformed to a view.
You should be able to ignore the class inside OnModelCreating
like so:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<DocumentDto>();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.