Issue fixed. It was a bad configuration server due to ASP.NET CORE 3.1 migration.
Hi @ismcagdas,
Thanks for your try. I don't use value converters so much but it is very useful sometimes. As it is a production app, I wanted to avoid creating new tables and manage data migrations...
I found a workarround by querying directly in SQL.
I used following code :
private IQueryable<Address> GetIQueryable(GetAllAddressesInput input)
{
//Due to EF CORE 3.X query limitations with value converters, tags must be queried directly in SQL
//TODO : revert to normal LINQ query when EF CORE 3.X limitations will be fixed
if (!input.Tags.IsNullOrEmpty())
{
string tagSearch = input.Tags;
if (!tagSearch.Contains(";"))
{
return _dbContextProvider.GetDbContext(Abp.MultiTenancy.MultiTenancySides.Tenant).Addresses.FromSqlRaw(
"SELECT * FROM dbo.LsAddresses WHERE TenantId = {0} and {1} IN(SELECT value FROM STRING_SPLIT(TagList, ';'))", AbpSession.TenantId, tagSearch
);
}
else
{
string query = String.Format("SELECT * FROM dbo.LsAddresses WHERE TenantId = {0}", AbpSession.TenantId);
foreach(var tagSplited in tagSearch.Split(";"))
{
query = query + String.Format(" and {0} IN(SELECT value FROM STRING_SPLIT(TagList, ';'))", tagSplited);
}
return _dbContextProvider.GetDbContext(Abp.MultiTenancy.MultiTenancySides.Tenant).Addresses.FromSqlRaw(query);
}
}
else
return _addressRepository.GetAll();
}
public async Task<PagedResultDto<GetAddressForViewDto>> GetAll(GetAllAddressesInput input)
{
var filteredAddresses = GetIQueryable(input)
.WhereIf(!string.IsNullOrWhiteSpace(input.Filter), e => false || e.Name.Contains(input.Filter) || e.City.Contains(input.Filter))
.WhereIf(!string.IsNullOrWhiteSpace(input.NameFilter), e => e.Name == input.NameFilter)
.WhereIf(!string.IsNullOrWhiteSpace(input.CityFilter), e => e.City == input.CityFilter);
var pagedAndFilteredAddresses = filteredAddresses
.OrderBy(input.Sorting ?? "id asc")
.PageBy(input);
var addresses = from o in pagedAndFilteredAddresses
select new GetAddressForViewDto()
{
Address = new AddressDto
{
Name = o.Name,
City = o.City,
Id = o.Id
}
};
var totalCount = await filteredAddresses.CountAsync();
return new PagedResultDto<GetAddressForViewDto>(
totalCount,
await addresses.ToListAsync()
);
}
You need to build an IQueryable first to search from tags, then you can continue with classic EF LINQ query.
Hope this can help others.
Great, tks @ismcagdas !
Hi @ismcagdas,
Tks. I send to you the demo projet and a video to [email protected]. Let me know if you need anything else.
Hi,
Yes I've tried to replace split method by creating a list<string> before query :
List<string> tagSearch = new List<string>();
if (!String.IsNullOrEmpty(input.Tags))
tagSearch = input.Tags.Split(";", StringSplitOptions.None).ToList();
then
.WhereIf( !input.Tags.IsNullOrEmpty(),
p => p.TagList != null && p.TagList.Any(t => tagSearch.Contains(t)));
but this is not the problem. From my understanding of EF Core 3.x, I need to split in several queries to avoid performance issues.... but I don't see how to do it.
I've tried to convert to SQL query but didn't get to it also...
Hi @ismcagdas,
Yes I check github repo for my other issues. I've fixed some queries by querying directly with SQL...
So here is the entity :
[Table("LsAddresses")]
public class Address : FullAuditedEntity<long>, IMustHaveTenant, IMustHaveSuccessiveId<long>
{
public virtual int TenantId { get; set; }
public virtual long SuccessiveId { get; set; }
[MaxLength(ConstForLength.Name)]
public virtual string Name { get; set; }
[MaxLength(ConstForLength.DescriptionTitle)]
public virtual string Title { get; set; }
public virtual IEnumerable<string> TagList { get; set; }
....
TagList property is converted in database with value converter :
var splitStringConverter = new ValueConverter<IEnumerable<string>, string>(v => string.Join(";", v), v => v.Split(new[] { ';' }));
//Tag list database converter
modelBuilder.Entity<Address>().Property(nameof(Address.TagList)).HasConversion(splitStringConverter);
As a consequence, TagList is stored in table column in a string with ';' delimiter.
The query in related appservice, including TagSearch, is this one : (TagSearch is string with ';' delimiter)
var addresses = _addressRepository
.GetAll()
.WhereIf(
!input.Filter.IsNullOrEmpty(),
p => p.Name.Contains(input.Filter)
)
.WhereIf(
input.Id != null,
p => p.Id == input.Id
)
.WhereIf(
!input.TagSearch.IsNullOrEmpty(),
p => p.TagList != null && p.TagList.Any(t => input.TagSearch.Split(";", StringSplitOptions.None).Contains(t)));
and input DTO is :
public class GetAddressesInput : PagedAndSortedInputDto, IShouldNormalize
{
public string Filter { get; set; }
public long? Id { get; set; }
public string TagSearch { get; set; }
public void Normalize()
{
if (Sorting.IsNullOrWhiteSpace())
{
Sorting = "CreationTime DESC";
}
}
}
Hi @joe704la,
I'm still using Azure DevOps which best fit my needs. You can do a lot of things but need to googlize and test a lot before putting it in production. I'm event using it for mobile apps.
The time you will need to set this up will be valuable !
Sure :
First connect to Zapier and try to authenticate to the app :
then my app login screen is displayed (specific Account controller just for third party app authentications)
After credential input, consent screen is displayed
At this stage, user is logged in and a cookie has ben stored
In this context, if user tries to connect again (for exemple to link zapier to another user account), it is not possible because previous user has not been logged out and consent screen is automatically displayed.
Then the question is how to log user out : after first consent or before displaying login screen ?
Thank you for your support. The reason of the logout issue was due to login from MVC (with identityserver) and angular in same browser : in fact, cookies are created from both MVC and angular that are hosted in same domain.
@maliming gave me 2 solutions : rebuild idenityserver UI on angular side (which was not my goal in this context) or trying to logout from MVC side before angular logout.
I've prefered second solution and it works like a charm.
For those who are searching for activating idenityserver authorization code flow UI with zero and angular, here is how I've done it. I've added a MVC UI build from IdentityServer QuickStart UI AND zero MVC project. The main difference is arround AccountController. I also had to change identityserver login URL to avoid duplicate routes with angular side.
I've just added login and consent screens as I don't needed more.
I still need to check if refresh token flow is working with identityserver.... but this is another topic.