How can we bind nested properties from query?
Example model:
public class X {
public string PropA { get;set; }
public string PropB { get;set: }
public Y ClassY { get;set; }
}
public class Y {
[FromQuery(Name = "prop1")]
public string Prop1 { get;set; }
public string Prop2 { get;set; }
}
Class x gets passed into the Controller Action:
public async Task<IActionResult> Search([FromQuery]X classX)
Value of prop1 model y doesn't get bound -> class x is always null.
I would to build my query like so https://host.example/api/x?prop1=example
As an alternative I can flatten out the models but I'd like to avoid that.
Related
How use Bounded Context Pattern in AspnetBoilerplate, Is there any way to use to use Bounded Context Pattern in AspnetBoilerplate?
Connect with multiple database in ASP.NET ZERO/ASP.NET BOILERPLATE.
Note - Use seperate DB Context to use multiple Databases.
Step 1. Create modal class in "MultipleDbContextEfCoreDemo.Core" Project for your tables.
[Table ("tblStudent")] //Is in First Database
public class Student : Entity<long> {
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
protected Student () { }
}
[Table ("tblCourses")] //Is in Second Database
public class Courses : Entity<long> {
public int ID { get; set; }
public string CourseName { get; set; }
public string Standard { get; set; }
protected Courses () { }
}
Step 2. In same project("MultipleDbContextEfCoreDemo.Core" Project) create/use "MultipleDbContextEfCoreDemoConsts.cs" file to add Database Connection names.
public class MultipleDbContextEfCoreDemoConsts
{
public const string LocalizationSourceName = "MultipleDbContextEfCoreDemo";
public const string ConnectionStringName = "Default";
public const string SecondDbConnectionStringName = "Second";
}
Step 3. In "MultipleDbContextEfCoreDemo.EntityFrameworkCore" Project goto "EntityFrameworkCore" Folder and create individual "DBContext" and "DbContextConfigurer" file for each database connection to which you want to connect.
FirstDatabase Setting -
required files to connect to first db -
1. FirstDbContext.cs
public class FirstDbContext : AbpDbContext, IAbpPersistedGrantDbContext {
/* Define an IDbSet for each entity of the application */
public DbSet<PersistedGrantEntity> PersistedGrants { get; set; }
public virtual DbSet<Student> Student { get; set; }
public FirstDbContext (DbContextOptions<FirstDbContext> options) : base (options) {
}
protected override void OnModelCreating (ModelBuilder modelBuilder) { }
}
2. FirstDbContextConfigurer
public static class FirstDbContextConfigurer {
public static void Configure (DbContextOptionsBuilder<FirstDbContext> builder, string connectionString) {
builder.UseSqlServer (connectionString);
}
public static void Configure (DbContextOptionsBuilder<FirstDbContext> builder, DbConnection connection) {
builder.UseSqlServer (connection);
}
}
SecondDatabase Setting -
required files to connect to second db -
1. SecondDbContext.cs
public class SecondDbContext : AbpDbContext, IAbpPersistedGrantDbContext {
/* Define an IDbSet for each entity of the application */
public DbSet<PersistedGrantEntity> PersistedGrants { get; set; }
public virtual DbSet<Student> Student { get; set; }
public SecondDbContext (DbContextOptions<SecondDbContext> options) : base (options) {
}
protected override void OnModelCreating (ModelBuilder modelBuilder) { }
}
2. SecondDbContextConfigurer
public static class SecondDbContextConfigurer {
public static void Configure (DbContextOptionsBuilder<SecondDbContext> builder, string connectionString) {
builder.UseSqlServer (connectionString);
}
public static void Configure (DbContextOptionsBuilder<SecondDbContext> builder, DbConnection connection) {
builder.UseSqlServer (connection);
}
}
Step 4. Then in same project("MultipleDbContextEfCoreDemo.EntityFrameworkCore") add "MyConnectionStringResolver.cs"
public class MyConnectionStringResolver : DefaultConnectionStringResolver
{
public MyConnectionStringResolver(IAbpStartupConfiguration configuration)
: base(configuration)
{
}
public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
if (args["DbContextConcreteType"] as Type == typeof(SecondDbContext))
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
return configuration.GetConnectionString(MultipleDbContextEfCoreDemoConsts.SecondDbConnectionStringName);
}
return base.GetNameOrConnectionString(args);
}
}
Step 5. Then in same project("MultipleDbContextEfCoreDemo.EntityFrameworkCore"), Update "MultipleDbContextEfCoreDemoEntityFrameworkCoreModule.cs" file to replace the "IConnectionStringResolver" with our custom implementation MyConnectionStringResolver.
[DependsOn(typeof(MultipleDbContextEfCoreDemoCoreModule), typeof(AbpEntityFrameworkCoreModule))]
public class MultipleDbContextEfCoreDemoEntityFrameworkCoreModule : AbpModule
{
public override void PreInitialize()
{
Configuration.ReplaceService<IConnectionStringResolver, MyConnectionStringResolver>();
// Configure first DbContext
Configuration.Modules.AbpEfCore().AddDbContext<FirstDbContext>(options =>
{
if (options.ExistingConnection != null)
{
FirstDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
}
else
{
FirstDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
}
});
// Configure second DbContext
Configuration.Modules.AbpEfCore().AddDbContext<SecondDbContext>(options =>
{
if (options.ExistingConnection != null)
{
SecondDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
}
else
{
SecondDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
}
});
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(MultipleDbContextEfCoreDemoEntityFrameworkCoreModule).GetAssembly());
}
}
Step 6. Create the Service in "MultipleDbContextEfCoreDemo.Application" project with Dto, Interface and Service Class.
ITestAppService.cs-
public interface ITestAppService : IApplicationService
{
List<string> GetStudentAndCourses();
}
TestAppService.cs
public class TestAppService : MultipleDbContextEfCoreDemoAppServiceBase, ITestAppService
{
private readonly IRepository<Student> _studentRepository; //in the first db
private readonly IRepository<Courses> _coursesRepository; //in the second db
public TestAppService(
IRepository<Student> studentRepository,
IRepository<Courses> coursesRepository
)
{
_studentRepository = studentRepository;
_coursesRepository = coursesRepository;
}
//a sample method uses both databases concurrently
public List<string> GetStudentAndCourses()
{
List<string> names = new List<string>();
var studentNames = _studentRepository.GetAllList().Select(p => "Student: " + p.FirstName).ToList();
names.AddRange(peopleNames);
var courseNames = _coursesRepository.GetAllList().Select(p => "Course: " + p.CourseName).ToList();
names.AddRange(courseNames);
return names;
}
}
Step 7. Add Database connectionStrings to your MultipleDbContextEfCoreDemo.Web/MultipleDbContextEfCoreDemo.Web.Host project's
"appsettings.json" file.
{
"ConnectionStrings": {
"Default":
"Server=XXX.XXX.XX.XX;Database=firstDB;Integrated Security=False;TrustServerCertificate=True;User ID=XX;Password=XXX;",
"Second":
"Server=XXX.XXX.XX.XX;Database=secondDB;Integrated Security=False;TrustServerCertificate=True;User ID=XX;Password=XXX;"
}
}
Step 8. Use Service in your angular/MVC project.
With EF you need one complete dbcontext for migrations.
Create other "bounded" dbcontext, with the entities to be ignored in modelbuilder, then use this in appservice.
Very simple answer
HTH
I have a Web API where one of the methods in a controller return true or false when validating user id which is a string of numbers. I do no have an actual database yet, so I sort of mocked up the set of values in the repository.
Below is my code:
My repository class:
public class myRepository
{
public myClasses.Employee[] GetAllEmployees()
{
return new myClasses.Employee[]
{
new myClasses.Employee
{
empId="111111",
empFName = "Jane",
empLName="Doe"
},
new myClasses.Employee
{
empId="222222",
empFName = "John",
empLName="Doe"
}
};
}
public bool VerifyEmployeeId(string id)
{
myClasses.Employee[] emp = new myClasses.Employee[]
{
new myClasses.Employee
{
empId="111111",
empFName = "Jane",
empLName="Doe"
},
new myClasses.Employee
{
empId="222222",
empFName = "John",
empLName="Doe"
}
};
for (var i = 0; i <= emp.Length - 1; i++)
{
if (emp[i].empId == id)
return true;
}
return false;
}
}
and my model class:
public class myClasses
{
public class Employee
{
public string empId { get; set; }
public string empFName { get; set; }
public string empLName { get; set; }
}
}
and here is my controller:
public class myClassesController : ApiController
{
private myRepository empRepository;
public myClassesController()
{
this.empRepository = new myRepository();
}
public myClasses.Employee[] GetEmployees()
{
return empRepository.GetAllEmployees();
}
public bool VerifyEmployee(string id)
{
return empRepository.VerifyEmployeeId(string id);
}
}
Now when i compile it I get an error:
} expected
Type or namespace definition, or end-of-file expected
; expected
in line
return empRepository.VerifyEmployeeId(string id);
of my controller.
My question is using boolean the best way to return Success or Failure from Web API method or is there a better way? and also why am I getting this error. I am new to Web API
The compile error is caused by this;
return empRepository.VerifyEmployeeId(string id);
You should rewrite to:
return empRepository.VerifyEmployeeId(id);
You don't have you specify the type of the argument when calling a function.
About returning true or false; if you intend to only check whether the employee is valid or not, I should leave it this way. If you plan to use that employee data more you could rewrite that function so it returns the actual employee itself, and return 404: Not Found when the Employee is not found for instance.
Get data from a derived class..
My samples.
public class Maintest
{
public string name = "2";
}
public class test : Maintest
{
string bla = name;
}
or
public class test : Maintest
{
test child = new test();
string bla = child.name;
}
Please reply
or
Share a link to explore
For example there is the main class
and I have a derived class that will output data of the first class.
As an example, I just wanted to pass the value of the derivative in the main class. For a proper understanding
If you return the field from a property, it might look a little something like this.
using System;
public class Program
{
public void Main()
{
var test = new Test();
Console.WriteLine(test.greeting);
}
}
// make this abstract if you're never directly instantiate MainTest
public abstract class MainTest
{
public string name = "world";
}
public class Test : MainTest
{
public string greeting {get { return "Hello " + name;}}
}
http://dotnetfiddle.net/R8kwh3
Also, you can enforce a contract by doing something like
public abstract class MainTest
{
public string name = "world";
// create an abstract property to ensure it gets implemented in the inheriting class
public abstract string greeting {get; private set;}
}
public class Test : MainTest
{
public override string greeting {get { return "Hello " + name;}}
}
Maybe you want get data in method? Therefore, you can use this:
public class test : Maintest
{
public string GetData()
{
return name;
}
}
While doing the following simple example, I found the following difficulties
As the title says, I am intending to use the Repository pattern while I am storing data in the Azure table storage.now I have couple of classes, Repository.cs, IRepository.cs, DataContext.cs and the Controller.
During my reading I found some info and been doing as follows.
IRepository.cs
public interface IRepository<T> where T: TableServiceEntity
{
T GetById(int Id);
IQueryable<T> GetAll();
}
and the DataContext.cs
public class DataContext<T>:TableServiceContext where T:TableServiceEntity
{
public DataContext(CloudStorageAccount storageaccount, StorageCredentials credentials)
: base(storageaccount.TableEndpoint.AbsoluteUri, credentials)
{
// _storageAccount = storageaccount;
var storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue(KEY_STORAGE));
storageAccount.CreateCloudTableClient().CreateTableIfNotExist(tableName);
}
public IQueryable<T> DeviceTable
{
get { return CreateQuery<T>(tableName); }
}
}
plus some part of the controller(I have already data in the table which I created before)
public class DeviceMeController : Controller
{
private IRepository<Entity>_repository;
public Controller() : this(new Repository<Entity>())
{
}
public Controller(IRepository<Entity> repository)
{
_repository = repository;
}
public ActionResult Index()
{
var List = _repository.GetAll();
return View(deviceList);
}
and the the Implementation of the interface Reposistory.cs, here is where I have an error and got lost somewhere
public class Repository<T>:IRepository<T> where T:TableServiceEntity
{
private DataContext<T> _serviceContext;
// here get tablename as pararameter;
// so the Enities call this function
public Repository()
{
// the same context for multiple tables ?
}
// perhaps it should take the table Name
public void Add(T item)
{
_serviceContext.AddObject(TableName,item);
_serviceContext.SaveChangesWithRetries();
}
public IQueryable<T> GetAll()
{
var results = from c in _serviceContext.Table
select c;
return results;
Error is about the null reference, the debugger shows the variable results is null?
In the end I need to know few things.
what should I do in the Repository.cs constructor? I believe the Datacontext.cs class has to be in a separate class ...
any Hint here
Hy,
first of all I presume you left out some code, because I don't see how you get your context in your repository. But supposing you do set it correctly, (injection?) taking into account the way you desinged your datacontext the repository doesn't need to know the table name because it is set in the following lines of code:
public IQueryable<T> DeviceTable
{
get { return CreateQuery<T>(Constants.DeviceTableName); }
}
So when you create a query based on the IQueryable DeviceTable, the table name is already set.
The thing is I don't see the need for your context class, especially as it can only bring over a single entity type (it is generic and based on an entity).
A basic layout of my Repository for Azure Table Storage is:
public abstract class CloudRepository<TEntity> : ICloudRepository<TEntity>
{
private TableServiceContext _tableServiceContext;
private string _tableName;
public string TableName
{
get { return _tableName ?? ( _tableName = typeof(TEntity).Name.Replace("Entity", string.Empty).ToLower()); }
}
public CloudStorageAccount StorageAccount
{
get
{
return CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString"));
}
}
public CloudTableClient TableClient
{
get
{
CloudTableClient cloudTableClient = StorageAccount.CreateCloudTableClient();
cloudTableClient.CreateTableIfNotExist(TableName);
return cloudTableClient;
}
}
public TableServiceContext ServiceContext
{
get
{
return _tableServiceContext ?? (_tableServiceContext = TableClient.GetDataServiceContext());
}
}
public IEnumerable<TEntity> FindAll()
{
return ServiceContext.CreateQuery<TEntity>(TableName).ToList();
}
}
Hope this helps you.
Not sure if what I am trying is possible or not, but I'd like to reuse a linq expression on an objects parent property.
With the given classes:
class Parent {
int Id { get; set; }
IList<Child> Children { get; set; }
string Name { get; set; }
}
class Child{
int Id { get; set; }
Parent Dad { get; set; }
string Name { get; set; }
}
If i then have a helper
Expression<Func<Parent,bool> ParentQuery() {
Expression<Func<Parent,bool> q = p => p.Name=="foo";
}
I then want to use this when querying data out for a child, along the lines of:
using(var context=new Entities.Context) {
var data=context.Child.Where(c => c.Name=="bar"
&& c.Dad.Where(ParentQuery));
}
I know I can do that on child collections:
using(var context=new Entities.Context) {
var data=context.Parent.Where(p => p.Name=="foo"
&& p.Childen.Where(childQuery));
}
but cant see any way to do this on a property that isnt a collection.
This is just a simplified example, actually the ParentQuery will be more complex and I want to avoid having this repeated in multiple places as rather than just having 2 layers I'll have closer to 5 or 6, but all of them will need to reference the parent query to ensure security.
If this isnt possible, my other thought was to somehow translate the ParentQuery expression to be of the given type so effectively:
p => p.Name=="foo";
turns into:
c => c.Dad.Name=="foo";
but using generics / some other form of query builder that allows this to retain the parent query and then just have to build a translator per child object that substitutes in the property route to the parent.
EDIT:
Following on from comments by #David morton
Initially that looks like I can just change from Expression to a delegate function and then call
.Where(ParentQuery()(c.Dad));
However I am using this in a wider repository pattern and cant see how I can use this with generics and predicate builders - I dont want to retrieve rows from the store and filter on the client (web server in this case). I have a generic get data method that takes in a base expression query. I then want to test to see if the supplied type implements ISecuredEntity and if it does append the securityQuery for the entity we are dealing with.
public static IList<T> GetData<T >(Expression<Func<T, bool>> query) {
IList<T> data=null;
var secQuery=RepositoryHelperers.GetScurityQuery<T>();
if(secQuery!=null) {
query.And(secQuery);
}
using(var context=new Entities.Context()) {
var d=context.GetGenericEntitySet<T>();
data=d.ToList();
}
return data;
}
ISecuredEntity:
public interface ISecuredEntity : IEntityBase {
Expression<Func<T, bool>> SecurityQuery<T>();
}
Example Entity:
public partial class ExampleEntity: ISecuredEntity {
public Expression<Func<T, bool>> SecurityQuery<T>() {
//get specific type expression and make generic
Type genType = typeof(Func<,>).MakeGenericType(typeof(ExampleEntity), typeof(bool));
var q = this.SecurityQuery(user);
return (Expression<Func<T, bool>>)Expression.Lambda(genType, q.Body, q.Parameters);
}
public Expression<Func<ExampleEntity, bool>> SecurityQuery() {
return e => e.OwnerId==currentUser.Id;
}
}
and repositoryHelpers:
internal static partial class RepositoryHelpers {
internal static Expression<Func<T, bool>> SecureQuery<T>() where T : new() {
var instanceOfT = new T();
if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) {
return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>();
}
return null;
}
}
EDIT Here is the (eventual) solution
I ended up going back to using expressions, and using LinqKit Invoke. Note: for EF I also had to call .AsExpandable() on the entitySet
The key part is being able to call:
Product.SecureFunction(user).Invoke(pd.ParentProduct);
so that I can pass in the context into my parent query
My end classes look like:
public interface ISecureEntity {
Func<T,bool> SecureFunction<T>(UserAccount user);
}
public class Product : ISecureEntity {
public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) {
return SecureFunction(user) as Expression<Func<T,bool>>;
}
public static Expression<Func<Product,bool>> SecureFunction(UserAccount user) {
return f => f.OwnerId==user.AccountId;
}
public string Name { get;set; }
public string OwnerId { get;set; }
}
public class ProductDetail : ISecureEntity {
public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) {
return SecureFunction(user) as Expression<Func<T,bool>>;
}
public static Func<ProductDetail,bool> SecureFunction(UserAccount user) {
return pd => Product.SecureFunction(user).Invoke(pd.ParentProduct);
}
public int DetailId { get;set; }
public string DetailText { get;set; }
public Product ParentProduct { get;set; }
}
Usage:
public IList<T> GetData<T>() {
IList<T> data=null;
Expression<Func<T,bool>> query=GetSecurityQuery<T>();
using(var context=new Context()) {
var d=context.GetGenericEntitySet<T>().Where(query);
data=d.ToList();
}
return data;
}
private Expression<Func<T,bool>> GetSecurityQuery<T>() where T : new() {
var instanceOfT = new T();
if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) {
return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>(GetCurrentUser());
}
return a => true; //returning a dummy query
}
}
Thanks for the help all.
You're overthinking it.
First, don't return an Expression<Func<Parent, bool>>, that'll require you to compile the expression. Return simply a Func<Parent, bool> instead.
Next, it's all in how you call it:
context.Children.Where(c => c.Name == "bar" && ParentQuery()(c.Dad));
context.Parents.Where(ParentQuery());