many to many select list data - c#

I would like to list the data with linq by combining the tables shown in the picture
expected result
using (CrmContext db = new Models.CrmContext())
{
_result = (from ct in db.CustomerDefination
join authorizedin db.authorized
on ct.customerId equals authorized.authorized
join authorizedDefination in db.authorizedDefination
on authorized.authorizedId equals authorizedDefination .authorizedId
select new
{
///I want to list the authorities belonging to
the customer
}).ToList();
}
There may be more than one authoritative definition of a customer. How can I do this in the LINQ query?

I spent WAY too much time doing your job, #IbrahimALPSOY:
Google Translating the turkish database screenshot,
Understanding which table holds what data using more Google Translate,
Understanding the expected result - which fields come from which tables,
Writing sample classes to represent the database,
Generating sample data for testing.
I wasted 30+ minutes before even starting to write a query. Next time I won't. Next time, you prepare your question such that, all other people could just copy your code and try queries right away.
This is the preparation:
class Authorisation
{
public int AuthorisationId; // yetkiliid
public int AccessId; // unvanid
public string AuthoriserName;
}
class Access
{
public int AccessId; // unvanid
public string AccessName;
}
class Customer
{
public int CustomerId; // musterid
public string CustomerName;
}
class Event
{
public int CustomerId;
public int AuthorisationId;
}
void Main()
{
var Customers = new[] {
new Customer() { CustomerId = 1, CustomerName = "Anne" },
new Customer() { CustomerId = 2, CustomerName = "Barbara" },
};
var Accesses = new[] {
new Access() { AccessId = 1, AccessName = "Read" },
new Access() { AccessId = 2, AccessName = "Write" },
};
var Authorisations = new[] {
new Authorisation() { AuthorisationId = 1, AuthoriserName = "The boss", AccessId = 1 }, // The boss can give read rights
new Authorisation() { AuthorisationId = 2, AuthoriserName = "The boss", AccessId = 2 }, // The boss can give write rights
new Authorisation() { AuthorisationId = 3, AuthoriserName = "A rookie", AccessId = 1 }, // A new employee can only give read rights
};
var Events = new[] {
new Event() { CustomerId = 1, AuthorisationId = 3 }, // A rookie let Anne read
new Event() { CustomerId = 1, AuthorisationId = 2 }, // While the boss let Anne write and scolded rookie
new Event() { CustomerId = 2, AuthorisationId = 1 }, // The boss thinks Barbara can't be trusted with write
};
}
I used this code instead of yours, because yours:
doesn't compile,
is illegible,
is badly formatted,
skips a table you've shown on your screenshot,
contains references to contexts only you have access to.
And here are the results:
Your query becomes feasible if you start with the table with non-unique keys:
from e in Events
join c in Customers on e.CustomerId equals c.CustomerId
join a in Authorisations on e.AuthorisationId equals a.AuthorisationId
join s in Accesses on a.AccessId equals s.AccessId
select new
{
e.CustomerId,
e.AuthorisationId,
c.CustomerName,
a.AuthoriserName,
s.AccessName
}
If this is not what you needed, modify my "preparation" to fit your question.

Related

How do I add a metric name to MetricOrderBy when using Google.Analytics.Data.V1Beta?

I am writing come code in c# with Visual Studio 2022 to fetch Google G4 analytics data and cannot get one piece to work correctly. It is the part where a create an "OrderBy" list for the "RunReportRequest". I have defined 3 lists for each of dimensions, metrics & orderby
List<Dimension> Dlist = new();
List<Metric> Mlist = new();
List<OrderBy> Olist = new();
when I add items to the Dlist and MList using the following it works fine.
Dlist.Add(new Dimension { Name = "country" });
Mlist.Add(new Metric { Name = "activeUsers" });
when I try to add the OrderBy it fails.
OrderBy orderBy = new OrderBy();
orderBy.Metric.MetricName = "activeUsers";
orderBy.Desc = true;
Olist.Add(orderBy);
Although the program compiles and runs but stops at the line
orderBy.Metric.MetricName = "activeUsers";
with the error message
'Object reference not set to an instance of an object.'
I also tried another way
Olist.Add(new OrderBy { Metric = "activeUsers", Desc = true });
but this indicates an error
Cannot implicitly convert type "string" to "Google.Analytics.Data.V1Beta.OrderBy.Types.MetricOrderBy"
the code that creates the report request is as follows
var G4request2 = new RunReportRequest
{
Property = "properties/" + Grequest.PropertyId,
Dimensions = { Dlist },
Metrics = { Mlist },
DateRanges = { new DateRange { StartDate = Grequest.StartDate, EndDate = Grequest.EndDate } },
OrderBys = { Olist }
};
If I do not include the Olist and don't use the OrderBys = { Olist} line in the RunReportRequest the program works and the Google Date is retrieved (just not sorted in the order I want though).
Can anyone help me with a suggestion how to fix the issue with the OrderBy part please?
Try using orderbys in the following manner:
RunReportRequest request = new RunReportRequest
{
Property = "properties/" + propertyId,
Dimensions = { new Dimension { Name = Dimensions },},
Metrics = { new Metric { Name = Matrix }, },
DateRanges = { new DateRange { StartDate = strStartDate, EndDate = strEndDate }, },
OrderBys = {new OrderBy {Dimension = new OrderBy.Types.DimensionOrderBy() { DimensionName = "date" }, Desc = false}, }
};
This gives the response sorted by date in ascending order.

How do i add more than one objects to a database using for each

i need to add an object to a database several times with a different Sku code.
I have the Sku code changing in the for each loop but i don't know how to add the products to the database in the same method without getting this error,
i think i might need to make it async but not sure how.
here is the code
public static PetersContext db = new PetersContext();
static void Main(string[] args)
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
Product p1 = new Product()
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 1,
SellPrice = 2,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
};
foreach (var size in sizeList)
{
p1.ProductSkus = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + size.ToString());
Console.WriteLine(p1.ProductSkus);
db.Products.Add(p1);
db.SaveChanges();
}
Console.ReadLine();
}
}
There are three things I would do differently in your approach.
You should create your context within a using statement.
Move the save changes function outside your for loop.
Create your p1 within the loop.
See the changes below:
using(var db = new PetersContext()) //open connection
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
foreach (var size in sizeList)
{
Product p1 = new Product() //Moving to inside of loop creates new instance every time
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 1,
SellPrice = 2,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
};
p1.ProductSkus = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + size.ToString());
Console.WriteLine(p1.ProductSkus);
db.Products.Add(p1);
}
db.SaveChanges(); //save changes after everything is done.
}
Console.ReadLine();
I think your issue is the fact your foreach loop is evaluating the query during the loop when you are trying to call Savechanges() which wants to generate another transaction.
If you just change your SizeList and Product queries to have .ToList() at the end, this will force evaluation and you will then be using lists in your foreach, not a transactional query.
Updated to reflect the comments:
Looking at your code it looks like the ProductSku is a property of Product - you should perhaps consider making SKU a separate table so that you don't need to repeat all the standard product properties. However, to give what I think you are asking for you need something like this;
static void Main(string[] args)
{
using (PetersContext db = new PetersContext()) {
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
foreach (var size in sizeList)
{
foreach (var product in products)
{
Product newProduct = new Product()
{
ProductSkus = (product.ProductCode + product.ProductBrand.ToString() + product.ProductColour.ToString() + size.ToString()),
ProductBrand = product.ProductBrand,
ProductCode = product.ProductCode,
CostPrice = product.CostPrice,
SellPrice = product.SellPrice,
ProductDescription = produce.ProductDescription,
ProductSeason = product.ProductSeason,
ProductType = product.ProductType
};
Console.WriteLine(p1.ProductSkus);
db.Products.Add(newProduct);
}
}
db.SaveChanges();
Console.ReadLine();
}
}
There are other changes you could make, but the the big one I'm seeing is there's only one product reference. That same product object is added to the product collection several times in a loop. Each time, the loop also sets a new Sku... but since they are all the same object, the references from prior iterations of the loop reflect the new data.
To fix this, you need a new product object each time through the loop. You can offset that performance by moving the db.SaveChanges() call to after the loop.
Thanks for yer help people,
I created a new object in the for each, changed the sku in the new object, i forgot i need to give the products a size so i did that in a linq query in the same loop and added it to a product list, the product was then looped through in another for each and added to the db, it could do with a refactoring but it will do for now, thanks again guys
public static PetersContext db = new PetersContext();
static void Main(string[] args)
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var sizeIdList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1
select (ProductSizes.SizeId);
var products = from Product in db.Products
select Product;
Product p1 = new Product()
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 27,
SellPrice = 79,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
ProductColour=1
};
IList<Product> newProductList = new List<Product>();
foreach (var size in sizeList)
{
string newSku = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + (size.ToString()));
Product newProduct = new Product()
{
ProductBrand = p1.ProductBrand,
ProductCode = p1.ProductCode,
CostPrice = p1.CostPrice,
SellPrice = p1.SellPrice,
ProductDescription = p1.ProductDescription,
ProductSeason = p1.ProductSeason,
ProductType = p1.ProductType,
ProductColour = p1.ProductColour,
ProductSkus= newSku,
};
newProduct.ProductSkus = newSku;
var SizeId =(from ProductSize in db.Sizes
where ProductSize.SizeDesc == size
select ProductSize.SizeId).First();
newProduct.ProductSize = SizeId;
newProductList.Add(newProduct);
}
foreach (var product in newProductList)
{
db.Products.Add(product);
db.SaveChanges();
}
Console.ReadLine();

LINQ Sub-selection

I'm a student, new to LINQ and we've been given an assignment to deal with LINQ queries.
My problem is I've been struggling the past days to figure out the correct way to perform this step: print the customers name that has "Milk" inside their orders.
Write a LINQ query to select all customers buying milk.
Print the Name of each customer in the query.
For the sake of time, here is the structure of the data so that you can understand it:
Product milk = new Product { Name = "Milk", Price = 13.02m };
Product butter = new Product { Name = "Butter", Price = 8.23m };
Product bread = new Product { Name = "Bread", Price = 17.91m };
Product cacao = new Product { Name = "Cacao", Price = 25.07m };
Product juice = new Product { Name = "Juice", Price = 17.03m };
Customer c1 = new Customer { Name = "x", City = "g", Orders = new Order[] {
new Order { Quantity = 2, Product = milk },
new Order { Quantity = 1, Product = butter },
new Order { Quantity = 1, Product = bread }
}
};
Customer c2 = new Customer { Name = "y", City = "g", Orders = new Order[] {
new Order { Quantity = 1, Product = cacao },
new Order { Quantity = 1, Product = bread },
new Order { Quantity = 2, Product = milk },
new Order { Quantity = 2, Product = butter },
}
};
Customer c3 = new Customer { Name = "z", City = "g", Orders = new Order[] {
new Order { Quantity = 3, Product = juice }
}
};
Customer[] customers = new Customer[] { c1, c2, c3 };
As an example of the syntax I'm using with LINQ here is a reference of working code:
var QueryCustomerByCity = from cus in customers.AsEnumerable()
where cus.City == "g"
select cus;
foreach (Customer c in QueryCustomerByCity)
Console.WriteLine("Customer {0} lives in {1}", c.Name, c.City);
I'm really trying hard to understand what's happening, so if you can help me please explain me how you reached such conclusion :)
Thank you a lot for your time!
Your current query of:
var QueryCustomerByCity = from cus in customers.AsEnumerable() //for each customer in customers
where cus.City == "g" // where the Customer is from the City "g"
select cus; // select the customer
reads as "for each customer represented as cus in the customers array where the customers City is "g" then retain the customer". So, as a result you'll have a sequence of customers where their City is "g".
As for your task, you're after is this:
var result = from cus in customers // for each customer in customers
where cus.Orders.Any(o => o.Product.Name == "Milk") // where the product name is "Milk"
select cus; // select the customer
This essentially goes over the customers in the customers array and checks their orders and if there's any product which has the name "Milk" retains that specific customer.
A LINQ query is a pipeline: Data flows in a stream, element by element, from one clause to the next. FROM some data source you can get a flow of elements (e.g., via AsEnumerable). From a flow of elements you can filter on some predicate (function on an element returning a boolean), keeping in the flow only those elements that the predicate accepts (returns true for) and throwing away the others. The filter is called WHERE and the predicate is the rest of the WHERE clause, e.g., WHERE cus.City == "G". And also, from elements in the flow, you can select a single field, changing the flow from a flow of the (original) element to a flow of the field. This is a SELECT, as in SELECT g.City where the flow would change from a stream of customers to a stream of strings (each of which was a city name).
From this (and the other LINQ clauses you can learn) you can construct whatever data pipeline you wish. Specifically, one that returns a stream of customer names given that the customer has ordered milk.

Concatenate Int to String in LINQ Query

I have the following LINQ query.
var providers = from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id };
I'm trying to concatenate the ID to "C". So, for example, if c.Id is 35 then the result should be "C35".
This obviously doesn't work because you can't add an integer (c.Id) to a string. I could easily resolve this in C# using string.Format() or converting the type. But how can I do this in LINQ?
Try using SqlFunctions.StringConvert Method:
var xd = (from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + SqlFunctions.StringConvert((double)c.Id).Trim()});
When you need functionality of .NET only in preparing the result (as opposed to, say, filtering, which should be done on RDBMS side to avoid bringing too much data in memory) the common trick is to complete the conversion in memory using the AsEnumerable method:
var providers = Repository.Query<Company>()
.Where(c => !c.IsDeleted)
.Select(c => new { c.Description, c.Id }) // <<== Prepare raw data
.AsEnumerable() // <<== From this point it's LINQ to Object
.Select(c => new { c.Description, Id = "C"+c.Id }); // <<== Construct end result
The code that you have written will work fine. Here is a mock up of the same code and it outputs the Id's
class Company
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
static void Main()
{
//setup
var list = new List<Company>();
list.Add(new Company
{
Description = "Test",
Id = 35,
IsDeleted = false
});
list.Add(new Company
{
Description = "Test",
Id = 52,
IsDeleted = false
});
list.Add(new Company
{
Description = "Test",
Id = 75,
IsDeleted = true
});
/* code you are looking for */
var providers = from c in list
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id };
foreach (var provider in providers)
{
Console.WriteLine(provider.Id);
}
Console.ReadKey();
}
What about string format
var providers = from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id.ToString() };

Concat Two IQueryables with Anonymous Types?

I've been wrestling with this a little while and it's starting to look like it may not be possible.
I want to Concat() two IQueryables and then execute the result as a single query. I tried something like this:
var query = from x in ...
select new
{
A = ...
B = ...
C = ...
};
var query2 = from y in ...
select new
{
A = ...
B = ...
C = ...
};
var query3 = query.Concat(query2);
However, the last line gives me the following error:
'System.Linq.IQueryable' does not contain a definition for 'Concat' and the best extension method overload 'System.Linq.ParallelEnumerable.Concat(System.Linq.ParallelQuery, System.Collections.Generic.IEnumerable)' has some invalid arguments
It appears it's expecting an IEnumerable for the argument. Is there any way around this?
It looks like I could resolve both queries to IEnumerables and then Concat() them. But it would be more efficient to create a single query, and it seems like that should be possible.
As you said previously in the comments, it seems that the two queries return different objects:
Query 1 (as per comment):
f__AnonymousTypee<Leo.Domain.FileItem,Leo.Domain.Employ‌​ee,int,string,string>
Query2 is
f__AnonymousTypee<Leo.Domain.FileItem,L‌​eo.Domain.Employee,int?,string,string>
This is why Concat is giving you an error message complaining about invalid arguments.
Anonymous objects will be equivalent types to other anonymous objects with the same property names and types declared in exactly the same order.
Assuming both query and query2 from from the same contexts, you should be able to combine the two, provided they are queries of equivalent types.
Your comment indicates that neither are of the same type.
query returns objects of type Anon<FileItem, Employee, int, string, string>
query2 returns objects of type Anon<FileItem, Employee, int?, string, string>.
You cannot combine the two because they are of different types. You'll need to make sure that both queries return objects of the same type.
var query = from x in ...
select new
{
A = (FileItem)...
B = (Employee)...
C = (int)...
...
};
var query2 = from y in ...
select new
{
A = (FileItem)...
B = (Employee)...
C = (int)...
...
};
The IDE determined query and query2 are of different types, while the IEnumerable<TSource> Concat<TSource>() extension method expects two same types (IEnumerable<TSource>). The three TSource's must be the same.
string[] strA = {"123", "234", "345"};
int[] intA = { 1, 2, 3 };
var query = from s in strA
select s;
var query2 = from i in strA // intA
select i;
var query3 = query.Concat(query2);
Uncomment "// intA" in VS and you'll see the difference.
Are you missing any namespace? Normally I mark my .NET Project Properties to target .net 4.0 for vs 2010. I do not use .net 4.0 Client Profile.
Please make sure that the types of A, B and C is matches in both the query anonymous types. Also the order of A, B and C should also match in both queries.
The following example works like a charm.
namespace Test
{
using System;
using System.Collections.Generic;
using System.Linq;
internal class Employee
{
public string Name { get; set; }
public int Age { get; set; }
public double Salary { get; set; }
public string Address { get; set; }
}
internal class Program
{
private static List<Employee> employees = new List<Employee>();
private static void BuildList()
{
employees.AddRange(
new Employee[]
{
new Employee() {Name = "Tom", Age = 22, Address = "sample1", Salary = 10000},
new Employee() {Name = "Mithun", Age = 27, Address = "sample1", Salary = 20000},
new Employee() {Name = "Jasubhai", Age = 24, Address = "sample1", Salary = 12000},
new Employee() {Name = "Vinod", Age = 34, Address = "sample1", Salary = 30000},
new Employee() {Name = "Iqbal", Age = 52, Address = "sample1", Salary = 50000},
new Employee() {Name = "Gurpreet", Age = 22, Address = "sample1", Salary = 10000},
}
);
}
private static void Main(string[] args)
{
BuildList();
var query = from employee in employees
where employee.Age < 27
select new
{
A = employee.Name,
B = employee.Age,
C = employee.Salary
};
var query2 = from employee in employees
where employee.Age > 27
select new
{
A = employee.Name,
B = employee.Age,
C = employee.Salary
};
var result = query.Concat(query2);
foreach (dynamic item in result.ToArray())
{
Console.WriteLine("Name = {0}, Age = {1}, Salary = {2}", item.A, item.B, item.C);
}
}
}
}

Categories

Resources