Getting all friends in DNN programmatically - dotnetnuke

I am trying to retrieve all the friends of a particular user. From the DNN RelationshipController I can only find the way to get the relationship between two users. Is it possible to get all the friends of a user?
'Get the relationship between two users:
DotNetNuke.Entities.Users.Social.RelationshipController.Instance.GetFriendRelationship(Me.UserInfo)

I had this code lying around. I get the current user, then get all relationships userIds that are friends (vs. follower) and are 'Accepted'. Using that list, I join back to the Users list to get the friend's user attributes and return as a new object.
Hope this helps.
private const int DNNSOCIAL_RELATIONSHIPTYPE_FRIEND = 1;
private const int DNNSOCIAL_RELATIONSHIPTYPE_FOLLOWER = 2;
public List<UserFriend> GetUserFriends(int portalid, int userid)
{
UserInfo currentUser = UserController.GetUserById(portalid, userid);
var friends = currentUser.Social.UserRelationships
.Where(r => r.RelationshipId == DNNSOCIAL_RELATIONSHIPTYPE_FRIEND
&& r.Status == RelationshipStatus.Accepted);
return (from f in friends
join u in UserController.GetUsers(portalid).Cast<UserInfo>()
on f.RelatedUserId equals u.UserID
select new UserFriend { UserId = u.UserID, DisplayName = u.DisplayName, ProfilePicUrl = u.Profile.PhotoURL }).ToList();
}
...
public class UserFriend
{
public int UserId { get; set; }
public string DisplayName { get; set; }
public string ProfilePicUrl { get; set; }
}
VB.NET version
Public Function GetUserFriends(portalid As Integer, userid As Integer) As List(Of UserFriend)
Dim currentUser As UserInfo = UserController.GetUserById(portalid, userid)
Dim friends = currentUser.Social.UserRelationships.Where(Function(r) r.RelationshipId = DNNSOCIAL_RELATIONSHIPTYPE_FRIEND AndAlso r.Status = Social.RelationshipStatus.Accepted)
Return (From f In friends
Join u In UserController.GetUsers(portalid).Cast(Of UserInfo)()
On f.RelatedUserId Equals u.UserID
Select New UserFriend With {.UserId = u.UserID, .DisplayName = u.DisplayName, .ProfilePicUrl = u.Profile.PhotoURL}).ToList()
End Function

Related

ASP.NET Core & Entity Framework Core : one-to-many relation and navigation properties

I am using ASP.NET Core and Entity Framework Core, and controller API for my react app database connect.
I have 4 classes Customer, Product, Store and Sales. The Customer, Product and Store table have a one-to-many relation with sales.
Sales class
public class Sales
{
[Key]
public int SalesId { get; set; }
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int StoreId { get; set; }
[DataType(DataType.Date)]
public string DateSold { get; set; }
public Product Product { get; set; }
public Customer Customer { get; set; }
public Store Store { get; set; }
}
Customer class
public class Customer
{
[Key]
public int CustomerId { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string Name { get; set; }
[Column(TypeName = "nvarchar(100)")]
public string Address { get; set; }
public IList<Sales> Sales { get; set; }
}
The other Product and store are same as customer class.
I run the migration command but the database was not created and command run successful so I created database and then I run the update-database which created all the tables in database.
If I add navigation properties to table will it fetch Sales record with Customer, Product and Store record as per ID in sales record.
I want to fetch sales record and in the sales table there is customer, product and store ID. How can I fetch their record?
My tables look like this:
I want to fetch sales record and in the sales table there is customer, product and store ID. How can I fetch their record?
Based on your model design , you could use Include method for loading related data like below:
Controller
[HttpGet]
public async Task<ActionResult<object>> GetSales()
{
var salesdata = await _context.Sales
.Include(s => s.Customer)
.Include(s => s.Product)
.Include(s => s.Store)
.Select(s => new
{
salesId = s.SalesId,
dateSold = s.DateSold,
customer = new
{
name = s.Customer.Name,
address = s.Customer.Address
},
product = new
{
name = s.Product.Name,
price = s.Product.Price
},
store = new
{
name = s.Store.Name,
address = s.Store.Address
}
})
.ToListAsync();
return salesdata;
}
For query syntax , you could use the Join in EF Core for Complex Query Operators
var data = from s in _context.Sales
join cu in _context.Customer on s.CustomerId equals cu.CustomerId
join p in _context.Product on s.ProductId equals p.ProductId
join st in _context.Store on s.StoreId equals st.StoreId
select new
{
salesId = s.SalesId,
dateSold = s.DateSold,
customer = new
{
name = s.Customer.Name,
address = s.Customer.Address
},
product = new
{
name = s.Product.Name,
price = s.Product.Price
},
store = new
{
name = s.Store.Name,
address = s.Store.Address
}
};
return await data.ToListAsync();
Result

Multi-mapping to the same table twice

How do you use Dapper's Multi-Mapping feature on two fields using the same table? i.e ClientInfo has two Address objects.
public class ClientInfo
{
public Guid Id => Guid.NewGuid();
public string FirstName { get; set; }
public string LastName { get; set; }
public Address PostalAddress { get; set; }
public Address BillingAddress { get; set; }
public int ContactNumber { get; set; }
}
public class Address
{
public Guid Id = Guid.NewGuid();
public string FirstLine { get; set; }
public string SecondLine { get; set; }
public string Town { get; set; }
public string PostCode { get; set; }
}
Tables
Relational - Address.Id used in ClientInfo.PostalAddress / BillingAddress
tbl.Address
|Id|FirstLine|SecondLine|Town|PostCode
tbl.ClientInfo
|Id|FirstName|LastName|PostalAddress|BillingAddress|etc..
Current implementation
Results only in all but PostalAddress being mapped.
var sql = #"select * from ClientInfo c left join Address as a on a.Id = c.PostalAddress left join Address as ad on ad.Id = c.BillingAddress";
var clients = connection.Query<ClientInfo, Address, Address, ClientInfo>(
sql,
(client, postal, billing) =>
{
client.PostalAddress = postal;
client.BillingAddress = billing;
return client;
},
splitOn: "PostalAddress,BillingAddress")
.Distinct()
.ToList();
return clients;
The splitOn parameter tells Dapper when/where to start mapping the next object, so you need to ensure that your SQL query returns the information in the correct order. Right now, you return 2 guids for PostalAddress and BillingAddress. Dapper doesn't know how to map them both.
select * from ... join ... will result in the Address data ordered AFTER the ClientInfo.PostalAddress and ClientInfo.BillingAddress columns.
Try: SELECT c.Id, c.FirstName, c.LastName, c.ContactNumber, a.*, ad.* FROM ClientInfo c LEFT JOIN Address AS a ON a.Id = c.PostalAddress JOIN Address AS ad ON ad.Id = c.BillingAddress
As you can see, removing the * effectively excludes the PostalAddress and BillingAddress guids from the results and we can now now splitOn: "Id,Id".
You will of course not have to provide the GUIDs in the select statement, this is just for the test to work.
[Test]
public void TestAbcd()
{
using (var dbConnection = new SqlConnection(_connectionString))
{
const string sql = #"WITH ClientInfo AS (
SELECT * FROM (
VALUES (#ci1, #adr1, #adr2), (#ci1, #adr3, #adr4)
) AS a (Id, PostalAddress, BillingAddress)
),
Address AS (
SELECT * FROM (
VALUES
(#adr1), (#adr2), (#adr3), (#adr4)
) AS a (Id)
)
select * from ClientInfo c left join Address as a on a.Id = c.PostalAddress left join Address as ad on ad.Id = c.BillingAddress";
dbConnection.Open();
var clients = dbConnection.Query<ClientInfo, Address, Address, ClientInfo>(
sql,
(client, postal, billing) =>
{
client.PostalAddress = postal;
client.BillingAddress = billing;
return client;
},
splitOn: "PostalAddress,BillingAddress", param: new {
ci1 = Guid.NewGuid(),
ci2 = Guid.NewGuid(),
adr1 = Guid.NewGuid(),
adr2 = Guid.NewGuid(),
adr3 = Guid.NewGuid(),
adr4 = Guid.NewGuid()
})
.Distinct()
.ToList();
}
}

How do I return one-to-many records in a specific order with Dapper and multi-mapping?

From Github:
Dapper allows you to map a single row to multiple objects. This is a
key feature if you want to avoid extraneous querying and eager load
associations.
Example:
Consider 2 classes: Post and User
> class Post {
> public int Id { get; set; }
> public string Title { get; set; }
> public string Content { get; set; }
> public User Owner { get; set; } }
>
> class User {
> public int Id { get; set; }
> public string Name { get; set; } }
Now let us say that we want to map a query that joins both the posts
and the users table. Until now
if we needed to combine the result of 2 queries, we'd need a new
object to express it but it makes more sense in this case to put the
User object inside the Post object.
When I do this (My classes are different names, but same construct), I get a Post and a User, a Post and a User. I'm using the Web API, so this is all JSON, if that matters. This is the way I'd see it if I did straight SQL in the Management Studio, you get the many rows and the corresponding User records
What if I want to send back the JSON that has the User once and all the posts in an array, then the next User, array of posts, etc.
id title content id name
1 Article1 Content1 55 Smith
2 Article2 Content2 55 Smith
3 Article3 Content3 55 Smith
I get the JSON back that has the User information over and over (as expected but not wanted). It's backwards.
What I want is a JSON object that has a format like this (I think this is correct):
{
"User": 55,
"Name": "Smith",
"Post": [
{
"id": 1,
"title": "title1",
"content":"MyContent1"
},
{
"id": 2,
"title": "title2",
"content":"MyContent2"
},
{
"id": 3,
"title": "title3",
"content":"MyContent2"
}
]
}
How do I do this? Right now I get the reverse. I thought I would simply change the classes around, but I did not because of the instructions on Github, the "makes more sense" part. I am using this,
(List<Post>)db.Query<Post, User, Paper>(sqlString, (post, user) => { post.user = user; return post; }, splitOn: "id");
I know I don't need the splitOn here, but in my real query the name is different than id.
This is pretty close:
https://www.tritac.com/developers-blog/dapper-net-by-example/
public class Shop {
public int? Id {get;set;}
public string Name {get;set;}
public string Url {get;set;}
public IList<Account> Accounts {get;set;}
}
public class Account {
public int? Id {get;set;}
public string Name {get;set;}
public string Address {get;set;}
public string Country {get;set;}
public int ShopId {get;set;}
}
var lookup = new Dictionary<int, Shop>()
conn.Query<Shop, Account, Shop>(#"
SELECT s.*, a.*
FROM Shop s
INNER JOIN Account a ON s.ShopId = a.ShopId
", (s, a) => {
Shop shop;
if (!lookup.TryGetValue(s.Id, out shop)) {
lookup.Add(s.Id, shop = s);
}
if (shop.Accounts == null)
shop.Accounts = new List<Account>();
shop.Accounts.Add(a);
return shop;
},
).AsQueryable();
var resultList = lookup.Values;
It makes the first object identifier. Not sure if I can use it like that or not. But this does do the array of books like I was asking, and I did not have to create a special object. Originally, it was supposed to be on Google Code, but I couldn't find this test on Github.
Since your SQL query is returning the flat records, i suggest you create a flat POCO and use dapper to map the result set to a collection of this. Once you have data in this collection, you can use LINQ GroupBy method to group it the way you want.
Assuming you have classes like
public class User
{
public int Id { set;get;}
public string Name { set;get;}
public IEnumerable<Post> Posts { set;get;}
}
public class Post
{
public int Id { set;get;}
public string Title{ set;get;}
public string Content { set;get;}
}
Now create the POCO for the flat result set row
public class UserPost
{
public int Id { set; get; }
public string Title { set; get; }
public string Content { set; get; }
public int UserId { set; get; }
public string Name { set; get; }
}
Now update your SQL query to return a result set with column name matching the above properties.
Now use Dapper to get the flat records
var userposts= new List<UserPost>();
using (var conn = new SqlConnection("YourConnectionString"))
{
userposts = conn.Query<UserPost>(query).ToList();
}
Now apply GroupBy
var groupedPosts = userposts.GroupBy(f => f.UserId, posts => posts, (k, v) =>
new User()
{
UserId = k,
Name = v.FirstOrDefault().Name,
Posts = v.Select(f => new Post() { Id = f.Id,
Title= f.Title,
Content = f.Content})
}).ToList();
Another option is to use .QueryMultiple
[Test]
public void TestQueryMultiple()
{
const string sql = #"select UserId = 55, Name = 'John Doe'
select PostId = 1, Content = 'hello'
union all select PostId = 2, Content = 'world'";
var multi = _sqlConnection.QueryMultiple(sql);
var user = multi.Read<User>().Single();
user.Posts = multi.Read<Post>().ToList();
Assert.That(user.Posts.Count, Is.EqualTo(2));
Assert.That(user.Posts.First().Content, Is.EqualTo("hello"));
Assert.That(user.Posts.Last().Content, Is.EqualTo("world"));
}
Update:
To return multiple users and their posts:
[Test]
public void TestQueryMultiple2()
{
const string sql = #"select UserId = 55, Name = 'John Doe'
select UserId = 55, PostId = 1, Content = 'hello'
union all select UserId = 55, PostId = 2, Content = 'world'";
var multi = _sqlConnection.QueryMultiple(sql);
var users = multi.Read<User>().ToList();
var posts = multi.Read<Post>().ToList();
foreach (var user in users)
{
user.Posts.AddRange(posts.Where(x => x.UserId == user.UserId));
}
Assert.That(users.Count, Is.EqualTo(1));
Assert.That(users.First().Posts.First().Content, Is.EqualTo("hello"));
Assert.That(users.First().Posts.Last().Content, Is.EqualTo("world"));
}

Dapper Multi-map next level

I'm using multiple mapping for a current query and now I need to map another object on the initial query.
For example:
public class Part {
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address {
public int Id { get; set; }
public string Street { get; set; }
public SiteOu Ou { get; set; }
}
public class SiteOu
public int Id { get; set; }
public string Name { get; set; }
}
Dapper:
connection.Query<Part, Address, Part>(sql, (part, address) => {
part.Address = address;
});
How do I get the Address class to have the SiteOu information?
This example isn't what I'm actually doing because I've actually got
Query<T1,T2,T3,T4,T5,TResult>();
I'm doing 1 select and 5 joins in my query. So hopefully I don't need more overloads of Query.
Dapper allows you to map a single row to multiple objects, so you can just map SiteOu as part of the same query.
[Test]
public void TestSplitOn()
{
var conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;Integrated Security=true;Initial Catalog=db");
conn.Open();
const string sql = "select Id = 1, Name = 'My Part', " +
"Id = 2, Street = 'My Street', " +
"Id = 3, Name = 'My Site'";
var result = conn.Query<Part, Address, SiteOu, Part>(sql, (part, address, siteOu) =>
{
part.Address = address;
address.Ou = siteOu;
return part;
},
commandType: CommandType.Text
).FirstOrDefault();
Assert.That(result, Is.Not.Null);
Assert.That(result.Address, Is.Not.Null);
Assert.That(result.Address.Ou, Is.Not.Null);
}
Important Note: Dapper assumes your Id columns are named "Id" or "id", if your primary key is different or you would like to split the wide row at point other than "Id", use the optional 'splitOn' parameter.
If you have more that 5 types to map, another out of the box option is to use QueryMultiple extension. Here is an example from the Dapper docs.
var sql =
#"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
Also check out this thread.

Cannot implicitly convert type 'System.Linq.IQueryable<AnonymousType#1>' to 'System.Collections.Generic.List

I am a newbie in .NET world, i was trying to achieve some result from LINQ. Please help.
Following is the error i am getting.
Error 4 Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?) ..Business\DAL\GlobalRequest.cs 39 27 Business
public ObservableCollection<AccountSummary> GetAccountSummary()
{
ObservableCollection<AccountSummary> list;
list = from ListData in
(from a in dbc.Accounts
join b in dbc.Ledgers on a.Account_ID equals b.Account_ID into t2
from t2Data in t2.DefaultIfEmpty()
where a.Is_Mannual == Convert.ToChar("Y")
select new
{
Account_ID = a.Account_ID,
Account_Name = a.Account_Name,
Amount = t2Data.Amount == null ? 0 : t2Data.Amount
}
).OrderBy(item => item.Account_Name)
group ListData by new
{
ListData.Account_ID,
ListData.Account_Name
} into GroupData
select new
{
Account_ID = GroupData.Key.Account_ID,
Account_Name = GroupData.Key.Account_Name,
Amount = GroupData.Sum(OL => OL.Amount)
};
}
class AccountSummary
{
public decimal AccountID { get; set; }
public string AccountName { get; set; }
public decimal Amount { get; set; }
}
Please advice.
Your query is projecting to a collection (sequence) of anonymous types:
select new
{
Account_ID = GroupData.Key.Account_ID,
Account_Name = GroupData.Key.Account_Name,
Amount = GroupData.Sum(OL => OL.Amount)
};
You will need to project to a collection of AccountSummary, e.g:
var accountSummaries =
...rest of the query here
select new AccountSummary
{
AccountId = ...,
AccountName = ...,
etc.
};
You can then create your observable collection from this collection of AccountSummary:
list = new ObservableCollection(accountSummaries);

Resources