DTO and ViewBag issue MVC C# - c#

Hi i have a problem getting data from two sources and combining into one DTO and then sending to the View
I have a list of Users in my Database and i have a list of Jobs in my WCF Service. What i am trying to do is make an Add Job page where i get all of the Users current Jobs from the Database and make a new DropDown list in the View that display all the Jobs a user doesnt have. So i intersect the two lists and create a new list with all obtained job removed.
I have tried doing it but cannot see where im going wrong.
public ActionResult AddJob(String usrCode)
{
var jobs = jobClient.getAllJobs();
var allJobCodes = (from s in jobs select s.jobCode).ToList();
var thisJobCode = (from s in db.UserJobs
where s.usrCode == usrCode
select s.jobCode).ToList();
var notObtained = allJobCodes.Except(thisJobCode);
IEnumerable<String> list1 = allJobCodes.AsEnumerable();
IEnumerable<String> list2 = notObtained.AsEnumerable();
IEnumerable<String> list3 = list2.Select(x => new UserJobsDTO()
{ jobCode = x });
IEnumerable<UserJobsDTO> list = list3(jobs, notObtained);
ViewBag.jobCode = new SelectList(list, "jobCode", "Description");
var model = new UserJobsDTO { usrCode = usrCode, jobCode = list};
return View("AddJob", model);
}
public class UserJobsDTO
{
public string usrCode { get; set; }
public IEnumerable<String> jobCode { get; set; }
public String Description { get; set; }
}
Can anyone help me out? The main problems at the moment are with jobCode = x complaining about....
Error 1 Cannot implicitly convert type
'string' to 'System.Collections.Generic.IEnumerable<string>'
And list3 variable complaining about this....
Error 2 'list3' is a 'variable' but is used like a 'method'

The errors state exactly what the problems are...
Cannot implicitly convert type
'string' to 'System.Collections.Generic.IEnumerable'
Job code is a collection:
public IEnumerable<String> jobCode { get; set; }
But you're trying to assign a non-collection value to it:
list2.Select(x => new UserJobsDTO()
{ jobCode = x })
So either you need to make jobCode a string (such that any given instance of the DTO has one value) or set the entire collection to it (such that any given instance of the DTO has the list of values).
'list3' is a 'variable' but is used like a 'method'
I literally have no idea what you're even trying to do here:
IEnumerable<UserJobsDTO> list = list3(jobs, notObtained);
But, as the error states, list3 is a variable not a method. You can't invoke a variable like a method.
It's very confusing what you're trying to accomplish in this code. And, honestly, the best advice at this time would just be to step through in a debugger, examine the runtime values that you have, and really think about how to structure those values for your view.
In particular, it will be very helpful to semantically discern between singular values and plural values. For example, terms like UserJobsDTO or jobCode imply certain levels of singularity and plurality, but their types and structures don't agree with that. (How can a collection of strings be a single "code"?)
Just the names and types of the structures you're using are very important in being able to logically understand and express what you're trying to do.

Related

getting properties of dynamic type c#

Consider the following code:
foreach (Type formType in allFormsToLoopThrough)
{
var nonPriorityForm = _context.Query(formType);
foreach (var nonpriority in nonPriorityForm)
{
var name = nonpriority.GetType().Name;
MyWorkAssignmentDTO form = new MyWorkAssignmentDTO
{
FormName = formType.Name
Id = nonpriority.Id
};
}
}
This snippet is looping thought a list of types.
Each type is taken from the list and passed to a Query function that returns an IQueryable - basically a list of records in a given table in a database that matches the type.
Then for each of the record sets that come back, I want to loop through those and from each create a new instance of MyWorkAssignmentDTO. I am only interested in a form name (which I can get from formType) but I cannot get nonpriority.Id
I know for sure that every nonpriority will have an Id once it is resolved in the loop.
What I can't do is implement this to work at run time.
Can anyone help?
I was able to use the dynamic keyword instead of var. While I lose compile time validation, this gets me over the line when I know for sure there will be an Id.
dynamic nonPriorityForm = _context.Query(formType);

I don't understand Dapper's mapping, multimapping and QueryMultiple

Title says it all, I'm trying to use it but I don't understand it. It's possible that the problem is a lack of knowledge due to that I'm an amateur, but I've read a dozen questions about this thing and googled for three days, and I still don't understand it.
I have SO many questions that I'm not sure that I should write it all in only one Question, or even if someone would read it all. If someone have other solution or think I should split it in different questions... well, I'm open to suggestions.
I was going to write an example, but again I read dozen of examples for days and didn't help me.
I just can't make my mind to understand how work something like the example at github:
var sql =
#"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});
So, Post have a property of type User and that property is called Owner, right? Something like:
public class Post
{
...
public User Owner { get; set;}
}
Therefore Query<Post, User, Post> will return a Post instance with all the properties and what not, AND will create a User instance and assign it to Post.Owner property? How would simple parameters be added to that query, for example if someone wanted to pass the id as a int parameter like ...WHERE Id = #Id", new {Id = id}, where should the parameter be added given that the parameter right now is (post, user) => { post.Owner = user; return post;}? The parameter always refer to the types given, you can only use the simple typical parameters for the dynamic query, both can be used simultaneously? How?
Also, how does it to differentiate what DB field goes to what object? It makes something like class name=DB table name? What happens if the classes don't have the same name as the DB table and I want to use the [Table] attribte, will it work or the attribute is only for Dapper.Contrib.Extensionsmethods? Would it work with objects that share the same DB table?
Regarding same table for different objects question, f.i. lets say I have a Person object that have a BankAccount object:
public class Person
{
...
public BankAccount Account {get; set;}
...
}
public class BankAccount
{
private string _Account;
public string Account
{
get { return _Account; }
set
{
if(!CheckIfIBANIsCorrect(value))
throw new Exception();
_Account = value;
}
}
private bool CheckIfIBANIsCorrect(string IBAN)
{
//...
//Check it
}
}
I could store the string account at the same table than Person, since every person would have a single account referred by the person's Id. How should I map something like that? Is there even a way, should I simply load the result in a dynamic object and then create all the objects, will Query create the rest of the Person object and I should bother to create the nested object myself?
And by the way, how is splitOnsupposedly be used in all this? I understand that it should split the result into various "groups" so you can split the results by Ids f.i. and take what you need, but I don't understand how should I retrieve the info from the different "groups", and how it return the different "groups", lists, enumerables, what?.
QueryMultiple is other thing that is FAR beyond my understanding regardles how much questions and answers I read.
You know... how the * does that .Read thing work? All I read here or googling assumes that Read is some sort of automagic thing that can miracly discern between objects. Again, do it divide results by class names so I just have to be sure every object have the correct table name? And again what happens with [Table] attribute in this case?
I think the problem I'm having is that I can't find(I suppose it doesn't exist) a single web page that describes it all(the examples at GitHub are very scarce), and I only still finding answers to concrete cases that doesn't answer exactly what I'm trying to understand but only that concrete cases, which are confusing me more and more while I read them, since everyone seems to use a bunch of different methods without explaining WHY or HOW.
I think that your main problem with the Dapper querying of joined table queries is thinking that the second argument in the list is always the "param" argument. Consider the following code:
var productsWithoutCategories = conn.Query<Product>(
"SELECT * FROM Products WHERE ProductName LIKE #nameStartsWith + '%'",
new { nameStartsWith = "a" }
);
Here, there are two arguments "sql" and "param" - if we used named arguments then the code would look like this:
var productsWithoutCategories = conn.Query<Product>(
sql: "SELECT * FROM Products WHERE ProductName LIKE #nameStartsWith + '%'",
param: new { nameStartsWith = "a" }
);
In your example, you have
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});
The second argument there is actually an argument called "map" which tells Dapper how to combine entities for cases where you've joined two tables in your SQL query. If we used named arguments then it would look like this:
var data = connection.Query<Post, User, Post>(
sql: sql,
map: (post, user) => { post.Owner = user; return post;}
);
I'm going to use the class NORTHWND database in a complete example. Say we have the classes
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public bool Discontinued { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
and we want to build a list of Products, with the nested Category type populated, we'd do the following:
using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
{
var productsWithCategories = conn.Query<Product, Category, Product>(
"SELECT * FROM Products INNER JOIN Categories ON Categories.CategoryID = Products.CategoryID,
map: (product, category) =>
{
product.Category = category;
return product;
},
splitOn: "CategoryID"
);
}
This goes through all the rows of JOIN'd Product and Category data and generates a list of unique Products but can't be sure how to combine the Category data with it, so it requires a "map" function which takes a Product instance and a Category instance and which must return a Product instance which has the Category data combined with it. In this example, it's easy - we just need to set the Category property on the Product instance to the Category instance.
Note that I've had to specify a "splitOn" value. Dapper presumes that the key columns of tables will simply be called "Id" and, if they are, then it can deal with joins on those columns automatically. However, in this case, we're joining on a column called "CategoryID" and so we have to tell Dapper to split the data back up (into Products and into Categories) according to that column name.
If we also wanted to specify "param" object to filter down the results, then we could do something like the following:
var productsWithCategories = conn.Query<Product, Category, Product>(
"SELECT * FROM Products INNER JOIN Categories ON Categories.CategoryID = Products.CategoryID WHERE ProductName LIKE #nameStartsWith + '%'",
map: (product, category) =>
{
product.Category = category;
return product;
},
param: new { nameStartsWith = "a" },
splitOn: "CategoryID"
);
To answer your final question, QueryMultiple simply executes multiple queries in one go and then allows you to read them back separately. For example, instead of doing this (with two separate queries):
using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
{
var categories = conn.Query("SELECT * FROM Categories");
var products = conn.Query("SELECT * FROM Products");
}
You could specify a single SQL statement that includes both queries in one batch, but you would then need to read them separately out of the combined result set that is returned from QueryMultiple:
using (var conn = new SqlConnection("Server=.;Database=NORTHWND;Trusted_Connection=True;"))
{
var combinedResults = conn.QueryMultiple("SELECT * FROM Categories; SELECT * FROM Products");
var categories = combinedResults.Read<Category>();
var products = combinedResults.Read<Product>();
}
I think that the other examples I've seen of QueryMultiple are a little confusing as they are often returning single values from each query, rather than full sets of rows (which is what is more often seen in simple Query calls). So hopefully the above clears that up for you.
Note: I haven't covered your question about the [Table] attribute - if you're still having problems after you've tried this out then I would suggest creating a new question for it. Dapper uses the "splitOn" value to decide when the columns for one entity end and the next start (in the JOIN example above there were fields for Product and then fields for Category). If you renamed the Category class to something else then the query will still work, Dapper doesn't rely upon the table name in this case - so hopefully you won't need the [Table] at all.

How to Seed a table with Index Unique Data Annotation based on 2 columns with one nullable

I want to create a table where data uniqueness is based on multiple columns (2 or 3) but one of them can be null.
For example:
FRUIT WEIGHT UNIT
Apple
Apple 1 Kg
Apple 2 Kg
Orange
Orange 1 Kg
Orange 2 Kg
will all be considered as unique entries.
Can it be done with EF 6.1 Data Annotations?
I beleive I achieved that like this:
[Required]
[Index("UniqueIndx", 1)]
public string Name { get; set; }
[Index("UniqueIndx", 2)]
public float? Weight { get; set; }
[Index("UniqueIndx", 3, IsUnique = true)]
public FormulUnit? Unit { get; set; }
which produces:
public override void Up()
{
AlterColumn("MyDb.Fruits", "Weight", c => c.Single());
AlterColumn("MyDb.Fruits", "Unit", c => c.Int());
CreateIndex("MyDb.Fruits", new[] { "Name", "Weight", "Unit" },
unique: true, name: "UniqueIndx");
}
From my understanding of Up method created by migration is that uniqueness is based on all 3 columns, not just last one I wrote that annotation to. This is ok for me, this is actually what I want.
I still have the problem of seeding that table. I'm getting errors on AddOrUpdate method like:
System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Nullable`1[System.Single]' and 'System.Single'.
for:
context.Fruits.AddOrUpdate(
p => new {p.Name, p.Weight, p.Unit}, fr1, fr2, fr3
);
Am I missing something?
Thanks
I just debugged the EF source code, and the code that throws the error is inside AddOrUpdate.
Here is part of the code excerpt that causes error.
var matchExpression
= identifyingProperties.Select(
pi => Expression.Equal(
Expression.Property(parameter, pi.Single()),
Expression.Constant(pi.Last().GetValue(entity, null))))
Or you can replicate the error by doing this.
var fr1 = new Fruit { Weight = 1 };
var epe = Expression.Property(Expression.Constant(fr1), "Weight");
var ec = Expression.Constant(fr1.Weight);
var ee = Expression.Equal(epe, ec);
I'm not sure why Float? is not acceptable by Expression.Equal, perhaps somebody else can explain.
But, if you can just use Add or manually checking whether to add or update, instead of AddOrUpdate, that will work.
UPDATE 2:
Below is what I thought my mistake and gave a possible sollution. But then again I was wrong. Although below code adds new records to databse with index based on 3 criteria, because it creates new objects of anonymous type during process, you loose foreign key relations.
If you have a parent class, let's call it Grocery which holds many Fruits, seed method will not update those relations with given code below.
Back to work agin...
My logic is wrong.
If we have a unique index based on multiple criteria, how one can tell which record to update? A record with one different criteria out of 3 might be very well a new record, or maybe it's an old one to be updated but that is not something compiler can tell.
Each record need to be added manualy.
Thank you Yuliam.
UPDATE:
For those of you who fall into same dilema, here is how I solved this situation (with help of other Stackoverflow posts)
First, you must know that Fruit entity has a base entity with ID, so that's why you don't see it here.
Then to understand better here are steps needed to take:
Get your DbSet from context into an anonymous type list, stripped
from properties that are not concern for comparison.
Put that anonymous type list into a strongly typed list (of your
entity type, in this example: Fruit)
Make a new list where you only select from your seed objects that don't exist in database.
Add all of those new objects to context (and then context.save() )
var SeedFruits = new List { fr1, fr2, fr3 };
var result = from a in context.Fruits
select new { a.Name, a.Weight, a.Unit };
List DBFruitsList = result.AsEnumerable()
.Select(o => new Fruit
{
Name = o.Name,
Weight = o.Weight,
Unit = o.Unit
}).ToList();
var UniqueFruitsList =
from ai in SeedFruits
where !DBFruitsList.Any(x => x.Name == ai.Name && x.Weight == ai.Weight && x.Unit == ai.Unit)
select ai;
foreach (Fruit fruittoupdate in UniqueFruitsList)
{
context.Fruits.Add(fruittoupdate);
}

Linq query to get all pending Loans

I have two tables in lightswitch LOANS(Id(default),..) and RELEASES(Id(default),Loan,..).i want to create a screen with all pending loans to be shown in a datagrid.so i created a wcf RIA class library.i wanto return all the loans that have no releases yet.what would be the linq query for that.
i tried this from other SO post but it gave a null reference exception.Nullreference exception was unhandled by user code.Object reference not set to an instance of object
Loan to Release has 1 : 0/1 (one loan to zero or one release)relationship
a loan can have zero or one relationship.a release must have a loan.
[Query(IsDefault = true)]
public IQueryable<PendingLoans> GetPendingLoans()
{
var res = from l in this.context.Loans
join r in this.context.Releases
on l equals r.Loan
where r.Loan == null
select new PendingLoans { BillNo = l.BillNo };
return res.AsQueryable<PendingLoans>();
}
Try this, this is linq but using lambdas instead of the query syntax
[Query(IsDefault = true)]
public IQueryable<PendingLoans> GetPendingLoans()
{
var res = this.context.Loans.Where(l=>!l.Releases.Any()).Select(l=> new PendingLoans { BillNo = l.BillNo }).AsQueryable();
return res;
}
If you want to use the query syntax this is effectively the same thing
[Query(IsDefault = true)]
public IQueryable<PendingLoans> GetPendingLoans()
{
var res = from l in this.context.Loans
where !l.Releases.Any()
select new PendingLoans { BillNo = l.BillNo };
return res.AsQueryable();
}
Now one thing I will say is that because you're converting to a PendingLoan before you say "AsQueryable", you've already enumerated over your data set (converting from one type of object to another). Therefore this will not have the same benefits of late bindings you may be trying to get out of the "AsQueryable" portion (You've already executed against the db), so you may be better off just returning an IEnumerable and forgetting about the AsQueryable part since you've already enumerated once.

Returning an object based on type

I have 5 different classes that all inherit from BaseEntity. I would like to create a new model class that will store information needed about one of these 5 classes as well as other identifiers.
When I retrieve the data for this new model from the database, all I get is a string with the class type along with an integer that represents which entry I can reference from the database.
For example, if I retrieve Id = 2, Type = "BaseBall". That means I will have need to use my BaseBallService to fetch the entry where Id == 2. If it happens to be Id = 2, Type = "BasketBall", then I will use BasketBallService.
Currently the only solution I can think of is it to have a bunch of if statements that evaluate the 'type' string. Depending on if the type matches a valid type (BaseBall, FootBall, BasketBall, etc.) then that object is returned.
Is there a way to easily do this without the need to define all 5 types in the model definition and stringing if or statements to identify this?
I hope I have identified the problem clearly enough. Let me know if any additional information is needed. I haven't written any code for this yet. I am merely trying to analyze the problem and form a solution.
I would just add a global enum at the project or solution level to store types. That way if you wish to add to it later you may without breaking any existing code as it is detached. But this may keep it well typed and thus demand a type that is listed from the end user or application. I did a simple console app to show this. You may apply the enum to any class not just a generic though. I also implement a return method to narrow down the return lists to show how I can get lists of my lists easier.
public enum types
{
Type1,
Type2,
Type3
}
public class GenericListing
{
public string Description { get; set; }
public types Type { get; set; }
}
class Program
{
public static List<GenericListing> GetTypeListing(List<GenericListing> aListings, types aTypes)
{
return aListings.Where(x => x.Type == aTypes).ToList();
}
static void Main(string[] args)
{
var stuff = new List<GenericListing>
{
new GenericListing {Description = "I am number 1", Type = types.Type1},
new GenericListing {Description = "I am number 2", Type = types.Type2},
new GenericListing {Description = "I am number 3", Type = types.Type3},
new GenericListing {Description = "I am number 1 again", Type = types.Type1},
};
string s = "";
GetTypeListing(stuff, types.Type1) // Get a specific type but require a well typed input.
.ForEach(n => s += n.Description + "\tType: " + n.Type + Environment.NewLine);
Console.WriteLine(s);
Console.ReadLine();
}
}
You may try using Dictionary, e.g.
Dictionary<String, BaseEntry> types = new Dictionary<String, BaseEntry>() {
{"BaseBall", new BaseBallService()},
{"BasketBall", new BasketBallService()},
...
}
...
var value = types["BaseBall"].GetId(2);

Categories

Resources