Hey guys, we are trying to implement the Chat Module for a Microservice based template. We added a new microservice and added the Chat module on this new microservice, however we are having the following issue:
ABP Framework version: v7.2.2
UI type: Angular
DB provider: EF Core
** Identity Server Separated (Angular): yes
Exception message and stack trace:
System.InvalidCastException: 'Unable to cast object of type 'Volo.Chat.Users.ChatUser' to type 'Volo.Abp.Users.IUserData'.'
System.InvalidCastException HResult=0x80004002 Message=Unable to cast object of type 'Volo.Chat.Users.ChatUser' to type 'Volo.Abp.Users.IUserData'. Source=System.Linq StackTrace: at System.Linq.Enumerable.<CastIterator>d__67
1.MoveNext() at System.Collections.Generic.List
1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source) at Volo.Abp.Users.UserLookupService`2.This exception was originally thrown at this call stack: [External Code] Volo.Chat.Users.ContactAppService.GetContactsAsync(Volo.Chat.Users.GetContactsInput) in ContactAppService.cs
Steps to reproduce the issue:"
We followed the guide https://docs.abp.io/en/commercial/latest/modules/chat
We are able to see the Chat module on the Angular, however when we click on the "start conversation" we get a 503 error. When debugging this method:
public virtual async Task<List
if (input.IncludeOtherContacts)
{
var lookupUsers = await _chatUserLookupService.SearchAsync(
nameof(ChatUser.UserName),
input.Filter,
maxResultCount: ChatConsts.OtherContactLimitPerRequest);
var lookupContacts = lookupUsers
.Where(x => !(conversationContacts.Any(c => c.Username == x.UserName) || x.Id == CurrentUser.GetId()))
.Select(x => new ChatContactDto
{
UserId = x.Id,
Name = x.Name,
Surname = x.Surname,
Username = x.UserName
});
conversationContacts.AddRange(lookupContacts);
}
return conversationContacts;
}
7 Answer(s)
-
0
Hi,
You can try this.
Update Chat Service project:
- Update the appsettings.json
"RemoteServices": { "AbpIdentity": { "BaseUrl": "https://localhost:44388/", Identity service URL "UseCurrentAccessToken": "false" } }, "IdentityClients": { "Default": { "GrantType": "client_credentials", "ClientId": "AdministrationService", "ClientSecret": "1q2w3e*", "Authority": "https://localhost:44322", AuthServer URL "Scope": "IdentityService", "RequireHttps": "true", "ValidateIssuerName": "true", "ValidateEndpoints ": "true" } },
- Grant user lookup permission to AdministrationService
You can create an application named
ChatService
instead of usingAdministrationService
- Add package references
<PackageReference Include="Volo.Abp.Identity.Pro.HttpApi.Client" Version="7.2.2" /> <PackageReference Include="Volo.Abp.Http.Client.IdentityModel.Web" Version="7.2.2" />
[DependsOn(typeof(AbpHttpClientIdentityModelWebModule))] [DependsOn(typeof(AbpIdentityHttpApiClientModule))] public class ...HttpApiHostModule : AbpModule
-
0
Hey, thanks for the answer.
I added what you mentioned to our Chat Service, however I have some questions:
1- When starting the websocket for the chat service we are having issue here:
We enabled the websocket for Ocelot too as we were having an issue connecting to the websocket and this was not mention on the tutorial of the page.
Is there something we need to specify or something for the web sockets on this module?
2- Even though we added the module and the previous steps you mention, I don't see the contacts loading on the left side. Debugging it also fails when bringing the Users from the Administration mode, I created another use just to check if in any of the methods on the GetContactsAsync to get contacts/users work but they return object reference or a 0.
_conversationRepository.GetListByUserIdAsync always returns 0,
_chatUserLookupService.SearchAsync throws an error.
"System.NullReferenceException HResult=0x80004003 Message=Object reference not set to an instance of an object. Source=Volo.Abp.IdentityModel StackTrace: at Volo.Abp.IdentityModel.IdentityModelAuthenticationService.<GetAccessTokenAsync>d__32.MoveNext() at Volo.Abp.IdentityModel.IdentityModelAuthenticationService.<GetAccessTokenOrNullAsync>d__31.MoveNext() at Volo.Abp.IdentityModel.IdentityModelAuthenticationService.<TryAuthenticateAsync>d__30.MoveNext() at Volo.Abp.Http.Client.IdentityModel.IdentityModelRemoteServiceHttpClientAuthenticator.<Authenticate>d__4.MoveNext() at Volo.Abp.Http.Client.IdentityModel.Web.HttpContextIdentityModelRemoteServiceHttpClientAuthenticator.<Authenticate>d__5.MoveNext() at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase
1.<RequestAsync>d__34.MoveNext() at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase
1.<RequestAsync>d__331.MoveNext() at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase
1.<RequestAsync>d__311.MoveNext() at Volo.Abp.Identity.IdentityUserLookupClientProxy.<SearchAsync>d__2.MoveNext() at Volo.Abp.Identity.HttpClientExternalUserLookupServiceProvider.<SearchAsync>d__6.MoveNext() at Volo.Abp.Users.UserLookupService
2.<SearchAsync>d__17.MoveNext() at Volo.Chat.Users.ContactAppService.<GetContactsAsync>d__3.MoveNext() in C:\Juvenal\NVC\code\testChat\services\FGChat\modules\Volo.Chat\src\Volo.Chat.Application\Volo\Chat\Users\ContactAppService.cs:line 44This exception was originally thrown at this call stack: [External Code] Volo.Chat.Users.ContactAppService.GetContactsAsync(Volo.Chat.Users.GetContactsInput) in ContactAppService.cs"
Is it possible for you guys to send me a working example of a chat service for a microservice solution? Just so I can compare and modify what ever I find as difference with my project.
-
0
Hi,
Could you share the project with me? I will check it. shiwei.liang@volosoft.com
-
0
Hi Liang, I was able to fix the issue, it was related with the Ocelot configuration.
I have another small quesiton here, I send a chat to another user but I don't see the icon changing to notify that he has unread messages, is this a known issue?
-
0
Hi,
It looks like a problem, we will check and fix it.
-
0
You can try this:
[ExposeServices(typeof(IConversationAppService))] public class MyConversationAppService : ConversationAppService { private readonly MyMessagingManager _myMessagingManager; private readonly IChatUserLookupService _chatUserLookupService; public MyConversationAppService( MessagingManager messagingManager, IChatUserLookupService chatUserLookupService, IConversationRepository conversationRepository, IRealTimeChatMessageSender realTimeChatMessageSender, IAuthorizationService authorizationService, MyMessagingManager myMessagingManager) : base(messagingManager, chatUserLookupService, conversationRepository, realTimeChatMessageSender, authorizationService) { _chatUserLookupService = chatUserLookupService; _myMessagingManager = myMessagingManager; } public override async Task<ChatConversationDto> GetConversationAsync(GetConversationInput input) { var targetUser = await _chatUserLookupService.FindByIdAsync(input.TargetUserId); if (targetUser == null) { throw new BusinessException("Volo.Chat:010003"); } var chatConversation = new ChatConversationDto { TargetUserInfo = new ChatTargetUserInfo { UserId = targetUser.Id, Name = targetUser.Name, Surname = targetUser.Surname, Username = targetUser.UserName, }, Messages = new List<ChatMessageDto>() }; var messages = await _myMessagingManager.ReadMessagesAsync(targetUser.Id, input.SkipCount, input.MaxResultCount); chatConversation.Messages.AddRange( messages.Select(x => new ChatMessageDto { Message = x.Message.Text, MessageDate = x.Message.CreationTime, ReadDate = x.Message.ReadTime ?? DateTime.MaxValue, IsRead = x.Message.IsAllRead, Side = x.UserMessage.Side }) ); return chatConversation; } } [ExposeServices(typeof(MessagingManager), typeof(MyMessagingManager))] public class MyMessagingManager : MessagingManager { private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IUserMessageRepository _userMessageRepository; private readonly IConversationRepository _conversationRepository; private readonly IMessageRepository _messageRepository; public MyMessagingManager( IMessageRepository messageRepository, IUserMessageRepository userMessageRepository, IChatUserLookupService chatUserLookupService, IConversationRepository conversationRepository, ICurrentUser currentUser, IUnitOfWorkManager unitOfWorkManager) : base(messageRepository, userMessageRepository, chatUserLookupService, conversationRepository, currentUser) { _messageRepository = messageRepository; _userMessageRepository = userMessageRepository; _conversationRepository = conversationRepository; _unitOfWorkManager = unitOfWorkManager; } public new async Task<List<MessageWithDetails>> ReadMessagesAsync(Guid targetUserId, int skipCount, int maxResultCount) { var messages = await _userMessageRepository.GetMessagesAsync(CurrentUser.GetId(), targetUserId, skipCount, maxResultCount); //TODO: Optimize var readMessages = new List<Message>(); foreach (var message in messages.Where(m => !m.UserMessage.IsRead).ToArray()) { message.UserMessage.MarkAsRead(Clock.Now); await _userMessageRepository.UpdateAsync(message.UserMessage); message.Message.MarkAsAllRead(Clock.Now); readMessages.Add(message.Message); } var conversationPair = await _conversationRepository.FindPairAsync(CurrentUser.GetId(), targetUserId); if (conversationPair != null) { conversationPair.SenderConversation.ResetUnreadMessageCount(); await _conversationRepository.UpdateAsync(conversationPair.SenderConversation); await _conversationRepository.UpdateAsync(conversationPair.TargetConversation); } try { using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: _unitOfWorkManager.Current?.Options.IsTransactional ?? false)) { foreach (var message in readMessages) { await _messageRepository.UpdateAsync(message); } await uow.CompleteAsync(); } } catch (AbpDbConcurrencyException e) { // The messages are change by another request. So, we can ignore this exception. } return messages; } }
-
0
Sorry for the wrong place in my previous answer.
We created an internal issue and it will fixed in the next patch version.