我很简单地需要从返回的集合中过滤掉所有的父母,在该集合中,字段上没有匹配项,即从字符串中按名称调用,与给出的值不匹配。我所追求的是如果parent
对象具有child
对象,并且该child
对象属性"foo"
(由字符串调用)不等于值{{1 }},bar
对象将从集合中适当过滤。
这是我的linq ef通话
parent
我希望看到的是类似SQL的
var field = "bar";
var values = new List<string>{"foo","fuYu"};
var dataPage = _aim_context.ae_s_bld_c.AsNoTracking();
var result = dataPage.Where(x =>
DbHelper.byPropertyContains(x.udfs, field, values)
);
// NOTE `udfs` is a ONE-to-ONE with `ae_s_bld_c`
我解决此问题的方法是设置一个表达式以使用SELECT [m].[id],[m.udfs].[bar],
FROM [dbo].[ae_s_bld_c] AS [m]
INNER JOIN [dbo].[ae_s_bld_c_udf] AS [m.udfs]
ON ([m].[multitenant_id] = [m.udfs].[multitenant_id])
WHERE ([m].[multitenant_id] = 1.0)
AND ([m.udfs].[bar] IN ('foo','fuYu')) --< Goal line
并编写SQL。我已经阅读了近50篇文章和SO帖子,但是由于每个人似乎都有不同的想法,所以还没有弄清楚我为什么还没有明白这一点,而且似乎大多数人都不符合dotnet core 2.1 +。
经过很多次迭代之后,我现在坐在这里。 注意:与我追求的目标有些不同。
我当前的环境linq尝试
List<string>
我认为最好是像我举的第一个示例那样,但这是我一直坚持的,因为我有一段时间将其与//...
dataPage = dataPage.Where(DbHelper.byPropertyContains<ae_s_bld_c>("udfs", field, values));
//...
对齐,两者都为{{1} }和x=>x.udfs
构建表达式的静态方法
x=> funName(x.udfs)
根据@NetMage的请求,我尝试向后使用LINQpad。我想我很亲密,但输出很难告诉。我把它放在这里供参考。明确地说,子级的属性名称将是名称的字符串。最好的结果是我可以有一个像x=> x.udfs.funName()
这样的名称,如果值包含字符串名,我可以在任何级别进行测试,但是从这里开始确实可以,
public static class DbHelper
{
public static Expression<Func<T, bool>> byPropertyContains<T>(string node, string field, List<string> value) {
//trying to take parent item and get it's property by string name because
// doing the function in linq like x=>x.udfs was not working right
// but that is the prefered I think
var property_parameter = Expression.Parameter(typeof(T), "x");
var property = Expression.PropertyOrField(property_parameter, node);
var selector_parameter = Expression.Parameter(property.Type, "y");
var selector = Expression.PropertyOrField(selector_parameter, field);
var methodInfo = typeof(List<string>).GetMethod("Contains", new Type[] {
typeof(string)
});
var list = Expression.Constant(value, typeof(List<string>));
var body = Expression.Call(methodInfo, list, selector);
return Expression.Lambda<Func<T, bool>>(body, selector_parameter);
}
}
答案 0 :(得分:1)
从这里开始。您需要类似这样的东西
field
其中var result = dataPage
.Where(x => values.Contains(EF.Property<string>(x.udfs, field)));
是按名称动态指定的字符串返回属性。
在EF Core中,您甚至不需要手动处理构建表达式,因为EF Core提供了一种特殊的SQL可翻译函数,用于通过名为EF.Property的名称访问简单属性。
使用该方法,解决方案很简单:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Configuration
@Order(2)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${security.signing-key}")
private String signingKey;
@Value("${security.encoding-strength}")
private Integer encodingStrength;
@Value("${security.security-realm}")
private String securityRealm;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/api/**").hasRole("admin").and().httpBasic()
.authenticationEntryPoint(authenticationEntryPoint());
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName(securityRealm);
return entryPoint;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
@Configuration
@Order(1)
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String LOGIN_PROCESSING_URL = "/login";
private static final String LOGIN_FAILURE_URL = "/login?error";
private static final String LOGIN_URL = "/login";
private static final String LOGOUT_SUCCESS_URL = "/logout";
private static final String LOGIN_SUCCESS_URL = "/user";
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user").hasAnyAuthority(Role.getAllRoles()).anyRequest().authenticated().and()
.formLogin().loginPage("/login").permitAll().and()
.logout().permitAll();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
//...)
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User currentUser(UserRepository userRepository) {
return userRepository.findByEmailIgnoreCase(SecurityUtils.getUsername());
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
@Bean()
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
}