Bind a class property to an instance property - c#

My hypothetical scenario:
In the New World Order, the single government mandates that all the banks should provide the same rate of interest to its customers. So all the bank should agree the rate of interest. Additionally a bank needs to have a policy
where it is open to changing(either increasing or decreasing) the interest
or not. If (at least) one bank is not open to changing the rate of interest, rate must not be modified until successful negotiations.
My C# program would look like below
namespace NewWorldOrder
{
public class Bank
{
public static float rate;
private bool allow_rate_modification;
private string bankname;
// Property
public string Bankname
{
// Assume some Business Logic is added to filter values
// Omitting those for brevity
get => bankname;
set => bankname = value;
}
// Property
public bool AllowRateModification
{
// Assume some Business Logic is added to filter values
// Omitting those for brevity
get => allow_rate_modification;
set => allow_rate_modification = value;
}
static Bank()
// To set the static field at runtime
{
// In actual case, this value may be initialized from a db
// Again doesn't matter how it is initialized.
rate = 4.5f;
}
// The ctor
public Bank(string bank_name_, bool allow_modification)
{
Bankname = bank_name_;
AllowRateModification = allow_modification;
}
}
class Program
{
static void Main(string[] args)
{
Bank rbs =new Bank("Royal Bank of Scotland",true);
Bank lloyds = new Bank("LLoyds", true);
Bank hsbc = new Bank("HSBC", false);
Bank.rate = 4.7f; // This should not happen as HSBC is not open to rate change
// Irrelevant Stuff
// ...
// ...
}
}
}
Long story short :
How can a static (class) property bind to one particular (usually boolean) instance property in all the instances. Or is there another logical approach
in C# by which this can be done?
Note: I am (very) new to C# , so please forgive me if this is complete blunder

Sounds like you need a list, linq can help you query it
var list = new List<Bank>()
{
new Bank("Royal Bank of Scotland", true),
new Bank("LLoyds", true),
new Bank("HSBC", false)
};
if (list.All(x => x.AllowRateModification))
{
// all banks Allow Rate Modification
}
You could use a class to manage the banks
public class Exchange
{
public List<Bank> Banks { get; set; } = new List<Bank>();
public void NegotiateRates()
{
while (!CanModifyRates)
{
// to the rate stuff in here
}
}
public bool CanModifyRates => Banks.All(x => x.AllowRateModification);
}
...
private static void Main()
{
var exchange = new Exchange();
exchange.Banks.Add(new Bank("Royal Bank of Scotland", true));
exchange.Banks.Add(new Bank("LLoyds", true));
exchange.Banks.Add(new Bank("HSBC", false));
exchange.NegotiateRates();
}
Additional Resources
List Class
Represents a strongly typed list of objects that can be accessed by
index. Provides methods to search, sort, and manipulate lists.
Enumerable.All(IEnumerable, Func) Method
Determines whether all elements of a sequence satisfy a condition.

Related

How to decide whether one class is a cohesive part vs dependency of another class (In terms of unit testing)?

I'm working on a project that was not designed with unit testing in mind.
Since inside of StartWorking() method I create a new instance of WorkYear and call year.RecalculateAllTime(), is WorkYear class considered to be an external dependency (in terms of unit testing)?
Or since the Employee bound to WorkYear by composition relationship + Employee is meant to perform actions on WorkYears, are the Employee and WorkYear form a cohesive entity where both classes aren't considered as dependencies of one another?
In other words, should StartWorking(...) method be tested in isolation from WorkYear class?
public abstract class Employee
{
private List<WorkYear> _workYears;
private readonly IntervalCalculator _intervalCalculator;
// Other fields...
protected Employee(IntervalCalculator intervalCalculator)
{
_intervalCalculator = intervalCalculator;
WorkYears = new List<WorkYear>();
}
public IEnumerable<WorkYear> WorkYears
{
get => _workYears.AsReadOnly();
private set => _workYears = value.ToList();
}
// Other properties...
public void StartWorking(DateTime joinedCompany)
{
List<PayPeriodInterval> allIntervals = _intervalCalculator.GenerateIntervalsFor(joinedCompany.Date.Year);
PayPeriodInterval currentInterval = allIntervals.Find(i => i.StartDate <= joinedCompany && joinedCompany <= i.EndDate);
PayPeriod firstPeriod = CalculateFirstPeriod(joinedCompany, currentInterval);
// There is a possibility that employee worked during this year and returned during
// the same exact year or even month. That is why we are trying to find this year in database:
WorkYear year = WorkYears.FirstOrDefault(y => y.CurrentYear == joinedCompany.Year);
if (year == null)
{
// Create new year with current and future periods.
year = new WorkYear(joinedCompany.Year, this, new List<PayPeriod> {firstPeriod});
AddYear(year);
}
else
{
// There is a possibility that employee left and got back during the same period.
// That is why we should try to find this period so that we don't override it with new one:
PayPeriod existingPeriod = year.GetPeriodByDate(joinedCompany);
if (existingPeriod != null)
{
var oldCurrentPeriodWorktime = new TimeSpan(existingPeriod.WorktimeHours, existingPeriod.WorktimeMinutes, 0);
firstPeriod = CalculateFirstPeriod(joinedCompany, currentInterval, oldCurrentPeriodWorktime);
}
year.PayPeriods.Add(firstPeriod);
}
List<PayPeriodInterval> futureIntervals = allIntervals.FindAll(i => currentInterval.EndDate < i.StartDate);
List<PayPeriod> futurePeriods = NewPeriods(futureIntervals);
year.PayPeriods.AddRange(futurePeriods);
year.RecalculateAllTime();
}
public abstract List<PayPeriod> NewPeriods(List<PayPeriodInterval> intervals);
public void AddYear(WorkYear workYear) => _workYears.Add(workYear);
protected abstract PayPeriod CalculateFirstPeriod(DateTime firstDayAtWork, PayPeriodInterval firstPeriodInerval, TimeSpan initialTime = default);
// Other methods...
}
public class WorkYear
{
public WorkYear(int currentYear, Employee employee, List<PayPeriod> periods)
{
Employee = employee;
EmployeeId = employee.Id;
CurrentYear = currentYear;
PayPeriods = periods ?? new List<PayPeriod>();
foreach (PayPeriod period in PayPeriods)
{
period.WorkYear = this;
period.WorkYearId = Id;
}
}
public int EmployeeId { get; }
public int CurrentYear { get; }
public Employee Employee { get; }
public List<PayPeriod> PayPeriods { get; set; }
// Other roperties...
public void RecalculateAllTime()
{
//Implementation Logic
}
}
Super big caveat: This stuff gets really opinionated really fast. There are lots of valid designs!
Okay, now that I've said that. DTO's (Data transfer objects) are not external dependencies. You don't inject them.
WorkYear has all the hallmarks of a DTO (other than that method). I think you are OK as is. Because it has that RecalculateAllTime method it should also be unit tested however. An example of an external dependency in this case would be something that fetches the list of work years.
The basic rule of thumb is:
You compose data (DTOs)
You inject behavior (services)

Is it possible to make array size size depended on the atribute value(int)? c#

Now my question is how can I make so when I make new object type Trip,
arrayOfPeople will be size of value numberOfRooms?
class Trip
{
private Person[] arrayOfPeople;
public Person[] arrayOfPeople get { return arrayOfPeople; }
set { arrayOfPeople = value; }
}
class Ship
{
private int numberOfRooms;
public int NumberOfRooms
{
get { return numberOfRooms; }
set { numberOfRooms = value; }
}
}
I was thinking of making numberOfRooms static and then in Trip constructor just setting arrayOfPeople = new Person[Ship.NumberOfRooms] but I am not sure if that is right aproach.
Feel free to correct me if I am wrong.
The comments in the code help to answer your question, so check it out :)
public class Trip
{
// Define a constructor for trip that takes a number of rooms
// Which will be provided by Ship.
public Trip(int numberOfRooms)
{
this.ArrayOfPeople = new Person[numberOfRooms];
}
// I removed the field arrayOfPeople becuase if you are just
// going to set and return the array without manipulating it
// you don't need a private field backing, just the property.
private Person[] ArrayOfPeople { get; set; }
}
public class Ship
{
// Define a constructor that takes a number of rooms for Ship
// so Ship knows it's room count.
public Ship(int numberOfRooms)
{
this.NumberOfRooms = numberOfRooms;
}
// I removed the numberOfRooms field for the same reason
// as above for arrayOfPeople
public int NumberOfRooms { get; set; }
}
public class MyShipProgram
{
public static void Main(string[] args)
{
// Create your ship with x number of rooms
var ship = new Ship(100);
// Now you can create your trip using Ship's number of rooms
var trip = new Trip(ship.NumberOfRooms);
}
}
Create a constructor for Trip that takes an integer parameter public Trip(int numberOfPeople) and inside that new up the array like you mentioned arrayOfPeople = new Person[numberOfPeople]()

Storing multiple type of data

I'm trying to create a quest system. I have QuestCreator, Quest and multiple objective classes that inherits an interface(TalkObjective, LocationObjective etc.)
In Quest class' constructor, I have created a list like List<IObjective>.
It didn't work.
Then I created a class to hold all different types of lists. But I lost the ability of ordering my objectives.
My question is; Is there a better way/design to do that?
[Edit]
I'm sorry that I didn't detailed it enough. Since I changed my code, I can't post it here. I tried to create the same code but this time the code is not giving me error. So I solve the problem on my own.
I was using a tutorial that wasn't completed/abandoned.
Here is the link to github
I built my Item/Inventory system with abstract classes and it was the first thing that came to my mind. But my intention was to create this quest system the way creator of the tutorial designed, so that I can learn his way.
I wanted to put objects of different Objective Classes in a list with the interface that they using in common way.
public class QuestCreator : MonoBehaviour {
#region fields
private List<IQuestObjective> objectives;
private GameObject itemManager;
private ItemDatabase itemdb;
private Location location;
private Quest quest;
//For Saving Quest
private Quest_data quests;
#endregion
void Update()
{
//Just for the test purpose
if (Input.GetKeyDown (KeyCode.E))
{
itemManager = GameObject.Find ("GameManager");
itemdb = itemManager.GetComponent<ItemDatabase>();
Item item = new Item ();
Item item2 = new Item ();
item = itemdb.weapon_database[0];
item2 = itemdb.weapon_database [1];
CollectionObjective collectionObjective = new CollectionObjective ("Find", 3, item, "Find this precious item");
CollectionObjective collectionObjective2 = new CollectionObjective ("Find", 1, item2, "Find Sword of Warrior from Dark Passage");
LocationObjective locationObjective = new LocationObjective ("Go to Green Valley", "Go to " + location, location, false);
objectives = new List<IQuestObjective> ();
objectives.Add(collectionObjective);
objectives.Add (collectionObjective2);
objectives.Add (locationObjective);
QuestText questText = new QuestText ();
QuestIdentifier questIdentifier = new QuestIdentifier();
questText.Title = "Finding Sword of Warrior";
questText.DescriptionSummary = "Summary...";
questText.Hint = "Hint...";
questIdentifier.QuestID = 1;
questIdentifier.SourceID = 1;
quest = new Quest (questIdentifier, questText, objectives);
Debug.Log (quest.Objectives[1].Description);
Debug.Log (quest.Objectives.Count);
}
}
You need to look into inheritance and polymorphism.
In your case you'd have a IObjective class that contains all the common logic:
public abstract IObjective : MonoBehaviour
{
public abstract void CommonMethod();
public virtual void OverrideIfNeeded(){}
public void UseAsIs(){}
}
CommonMethod has to be overrriden by subclass. OverrideIfNeeded may be overriden or used as it is. UseAsIs cannot be overriden (it can be hidden though).
Then you have a collection:
IEnumerable<IObjective> collection;
it contains all kind of different objects that are all IObjective and you can iterate and call all methods from the IObjective:
foreach(IObjective obj in collection)
{
obj.CommonMethod();
obj.UseAsIs();
...
}
Here is an example of the code you may have for your problem.
public class Program
{
public struct Location
{
// Assumes 2D game location
public int X;
public int Y;
}
public struct Character
{
public int GameCharId;
}
public class BaseObjective
{
public string Title;
public string Description;
}
public class TalkObjective : BaseObjective
{
public Character TargetCharacter;
}
public class LocationObjective : BaseObjective
{
public Location TargetLocation;
}
public static void Main(string[] args)
{
List<BaseObjective> currentObjectives = new List<BaseObjective>();
TalkObjective obj1 = new TalkObjective(){ Title = "Talk to Bob", Description = "Bob has some useful information for you", TargetCharacter = new Character(){GameCharId = 87}};
LocationObjective obj2 = new LocationObjective(){ Title = "Find the thing", Description = "Bob informed you of a thing, go and find it", TargetLocation = new Location(){ X = 33, Y=172}};
currentObjectives.Add(obj1);
currentObjectives.Add(obj2);
}
}
At first to all game programming, I'm highly suggesting going through this web book - http://gameprogrammingpatterns.com/contents.html (it also offers PDF/book variant for some money). It helps You with some game-pattern examples and You will get an idea how games are made.
Your question is kind of broad and related to each person opinion, which should not be listed as a Q on SO, however:
Logically to me it is like: There is 1 Quest (created by factory QuestCreator), which contains List<Objectives>.
Objective should be an abstract class, containing some variables and methods (Is objective done? - other things that all Objectives have in common).
After that You should inherit smaller objective (like TalkObjective, ItemObjective) and override inner implementation of the methods -> IsObjectiveDone.
On
To be honest, in game-programming, developers are stepping away to avoid inheritance as much as possible. It is too hard to create inheritance tree and then go through the code. Instead they are trying to rely on pattern like Component (same source as above).
Adding some example:
public abstract class Objective
{
public bool IsObjectiveDone { get; private set; }
public virtual void CheckIfDone();
}
public class ObjectiveGroup
{
public bool AllObjectivesDone => SubObjectives.All(a => a.IsObjectiveDone);
public Objective[] SubObjectives { get; private set; }
public static ObjectiveGroup Create(TypeOfQuest aType, Requirements[] aReq)
{ /* factory implementation */ }
}
Once You have the example above, You can define each type of "special" objective:
public class ItemObjective : Objective
{
public Item RequiredItem { get; private set; }
override public void CheckIfDone()
{
this.IsObjectiveDone = Player.GetInstance().Inventory.Contains(RequiredItem);
}
}
Once You will want to start new Quest, You will call the factory, which will create the Quest, containing group of objectives. On each objective You will CheckIfDone everytime user do some action/get new item or so.
public class Quest
{
public ObjectiveGroup { get; private set; }
public Quest(TypeOfQuest aType, Requirements[] aReq)
{
this.ObjectiveGroup = ObjectiveGroup.Create(aType, aReq);
}
}
public class Player
{
public List<Quest> Quests = new List<Quest>();
public List<Item> Inventory = new List<Item>();
}
public void Main(/* ... */)
{
Player player = new Player();
player.Quests.Add(new Quest(TypeOfQuest.ItemObtain, new Requirements[] { Item["Sword of Conan"] });
while(true)
{
player.Quests.ObjectiveGroup.ForEach(a => a.SubObjectives.ForEach(b => b.CheckIfDone()));
foreach(var objGrp in player.Quests.ObjectiveGroup)
if(objGrp.IsObjectiveDone) Console.WriteLine("Quest completed");
}
}
The better design, will be using Finite Automaton for this task, not some sort of list of objective.
So, your quest will be described with graph of predicates (conditions where to move to state or not, event listeners if you want) and states (accompishments in quest). For example, let's imagine hero entered some tavern, then he also enters some different quest lines. One of them describes town robber quest:
[Start] -(talked with barmen about robber)-> [Kill robber]
[Start] -(talked with robber wife) -> [Ask robber to return items]
//this is made for karma decision between two quest lines, so you are free to chose what to do with poor robber, take robber money or gain karma in town.
[Ask robber to return items] -(talked with barmen about robber)-> [Kill robber]
[Kill robber] -(talked with robber wife) -> [Ask robber to return items]
//barmen quest line
[Kill robber] -(robber killed)-> [Success quest (you can take money as reward)]
[Kill robber] -(robber spared)-> [Fail quest]
//wife quest line
[Ask robber to return items] -(robber convinced)-> [Success quest (you can now sleep with his wife for free)]
[Ask robber to return items] -(robber not convinced)-> [Ask robber to return items]
[Ask robber to return items] -(robber got bored of your questions)-> [Fail quest]
As you see this is all described with simple automaton rules and you can make pretty complex quests without much effort. In case of your list of objective you can't possibly branch your quest into different states, so only possible way to complete your quest is to meet ALL actions described one by one, even if it has two possible and successful outcomes.
Predicates in this example can be described as events, and states - as simple numbers or strings.
This is just very slow example of how I see it:
public class QAutomaton
{
private readonly Dictionary<string, Dictionary<string, string>> _graph = new Dictionary<string, Dictionary<string, string>>();
public void AddState(string state)
{
_graph.Add(state, new Dictionary<string, string>());
}
public void AddCondition(string from, string condition, string to)
{
_graph[from].Add(condition, to);
}
public string GetNext(string from, string condition)
{
var conds = _graph[from];
string nextState;
conds.TryGetValue(condition, out nextState);
return nextState;
}
}
public class Quest
{
public string CurrentState = "Start";
private readonly QAutomaton _automaton;
public Quest(QAutomaton automaton)
{
_automaton = automaton;
}
public void FeedEvent(string condition)
{
var nextState = _automaton.GetNext(CurrentState, condition);
if (nextState != null)
{
CurrentState = nextState;
}
}
}
public static void Main()
{
var fa = new QAutomaton();
fa.AddState("Start");
fa.AddState("Kill robber");
fa.AddState("Ask robber to return items");
fa.AddCondition("Start", "talked with barmen about robber", "Kill robber");
fa.AddCondition("Start", "talked with robber wife", "Ask robber to return items");
//describe rest here...
_quest = new Quest(fa);
}
public static void OnTalkedWithBarmenAboutRobberEventHandler()
{
_quest.FeedEvent("talked with barmen about robber");
var state = _quest.CurrentState;
if (state == "Kill robber")
{
//locate him on global map or something
}
}

Why is my game serializing this class?

So I'm making a game, and it saves users' progress on the computer in a binary file. The User class stores a few things:
Integers for stat values (Serializable)
Strings for the Username and the skin assets
Lists of both the Achievement class and the InventoryItem class, which I have created myself.
Here are the User fields:
public string Username = "";
// ID is used for local identification, as usernames can be changed.
public int ID;
public int Coins = 0;
public List<Achievement> AchievementsCompleted = new List<Achievement>();
public List<InventoryItem> Inventory = new List<InventoryItem>();
public List<string> Skins = new List<string>();
public string CurrentSkinAsset { get; set; }
The Achievement class stores ints, bools, and strings, which are all serializable. The InventoryItem class stores its name (a string) and an InventoryAction, which is a delegate that is called when the item is used.
These are the Achievement class's fields:
public int ID = 0;
public string Name = "";
public bool Earned = false;
public string Description = "";
public string Image;
public AchievmentDifficulty Difficulty;
public int CoinsOnCompletion = 0;
public AchievementMethod OnCompletion;
public AchievementCriteria CompletionCriteria;
public bool Completed = false;
And here are the fields for the InventoryItem class:
InventoryAction actionWhenUsed;
public string Name;
public string AssetName;
The source of the InventoryAction variables are in my XNAGame class. What I mean by this is that the XNAGame class has a method called "UseSword()" or whatever, which it passes into the InventoryItem class. Previously, the methods were stored in the Game1 class, but the Game class, which Game1 inherits from, is not serializable, and there's no way for me to control that. This is why I have an XNAGame class.
I get an error when trying to serialize: "The 'SpriteFont' class is not marked as serializable", or something like that. Well, there is a SpriteFont object in my XNAGame class, and some quick tests showed that this is the source of the issue. Well, I have no control over whether or not the SpriteFont class is Serializable.
Why is the game doing this? Why must all the fields in the XNAGame class be serializable, when all I need is a few methods?
Keep in mind when answering that I'm 13, and may not understand all the terms you're using. If you need any code samples, I'll be glad to provide them for you. Thanks in advance!
EDIT: One solution I have thought of is to store the InventoryAction delegates in a Dictionary, except that this will be a pain and isn't very good programming practice. If this is the only way, I'll accept it, though (Honestly at this point I think this is the best solution).
EDIT 2: Here's the code for the User.Serialize method (I know what I'm doing in inefficient, and I should use a database, blah, blah, blah. I'm fine with what I'm doing now, so bear with me.):
FileStream fileStream = null;
List<User> users;
BinaryFormatter binaryFormatter = new BinaryFormatter();
try
{
if (File.Exists(FILE_PATH) && !IsFileLocked(FILE_PATH))
{
fileStream = File.Open(FILE_PATH, FileMode.Open);
users = (List<User>)binaryFormatter.Deserialize(fileStream);
}
else
{
fileStream = File.Create(FILE_PATH);
users = new List<User>();
}
for (int i = 0; i < users.Count; i++)
{
if (users[i].ID == this.ID)
{
users.Remove(users[i]);
}
}
foreach (Achievement a in AchievementsCompleted)
{
if (a.CompletionCriteria != null)
{
a.CompletionCriteria = null;
}
if (a.OnCompletion != null)
{
a.OnCompletion = null;
}
}
users.Add(this);
fileStream.Position = 0;
binaryFormatter.Serialize(fileStream, users);
You cannot serialize a SpriteFont by design, actually this is possible (.XNB file) but it hasn't been made public.
Solution:
Strip it off your serialized class.
Alternatives:
If for some reasons you must serialize some font, the first thing that comes to my mind would be to roll-out your own font system such as BMFont but that's a daunting task since you'll have to use it everywhere else where you might already do ...
Generate a pre-defined amount of fonts (i.e. Arial/Times/Courier at size 10/11/12 etc ...) using XNA Content app (can't recall its exact name); then store this user preference as two strings. With a string.Format(...) you should be able to load the right font back quite easily.
Alternative 2 is certainly the easiest and won't take more than a few minutes to roll-out.
EDIT
Basically, instead of saving a delegate I do the following:
inventory items have their own type
each type name is de/serialized accordingly
their logic does not happen in the main game class anymore
you don't have to manually match item type / action method
So while you'll end up with more classes, you have concerns separated and you can keep your main loop clean and relatively generic.
Code:
public static class Demo
{
public static void DemoCode()
{
// create new profile
var profile = new UserProfile
{
Name = "Bill",
Gold = 1000000,
Achievements = new List<Achievement>(new[]
{
Achievement.Warrior
}),
Inventory = new Inventory(new[]
{
new FireSpell()
})
};
// save it
using (var stream = File.Create("profile.bin"))
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, profile);
}
// load it
using (var stream = File.OpenRead("profile.bin"))
{
var formatter = new BinaryFormatter();
var deserialize = formatter.Deserialize(stream);
var userProfile = (UserProfile) deserialize;
// set everything on fire :)
var fireSpell = userProfile.Inventory.Items.OfType<FireSpell>().FirstOrDefault();
if (fireSpell != null) fireSpell.Execute("whatever");
}
}
}
[Serializable]
public sealed class UserProfile
{
public string Name { get; set; }
public int Gold { get; set; }
public List<Achievement> Achievements { get; set; }
public Inventory Inventory { get; set; }
}
public enum Achievement
{
Warrior
}
[Serializable]
public sealed class Inventory : ISerializable
{
public Inventory() // for serialization
{
}
public Inventory(SerializationInfo info, StreamingContext context) // for serialization
{
var value = (string) info.GetValue("Items", typeof(string));
var strings = value.Split(';');
var items = strings.Select(s =>
{
var type = Type.GetType(s);
if (type == null) throw new ArgumentNullException(nameof(type));
var instance = Activator.CreateInstance(type);
var item = instance as InventoryItem;
return item;
}).ToArray();
Items = new List<InventoryItem>(items);
}
public Inventory(IEnumerable<InventoryItem> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
Items = new List<InventoryItem>(items);
}
public List<InventoryItem> Items { get; }
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
var strings = Items.Select(s => s.GetType().AssemblyQualifiedName).ToArray();
var value = string.Join(";", strings);
info.AddValue("Items", value);
}
#endregion
}
public abstract class InventoryItem
{
public abstract void Execute(params object[] objects);
}
public abstract class Spell : InventoryItem
{
}
public sealed class FireSpell : Spell
{
public override void Execute(params object[] objects)
{
// using 'params object[]' a simple and generic way to pass things if any, i.e.
// var world = objects[0];
// var strength = objects[1];
// now do something with these !
}
}
Okay, so I figured it out.
The best solution was to use a Dictionary in the XNAGame class, which stores two things: an ItemType (an enumeration), and an InventoryAction. Basically, when I use an item, I check it's type and then look up it's method. Thanks to everyone who tried, and I'm sorry if the question was confusing.

Returning a collection of objects where an objects property matches any property from another collection of objects using LINQ-to-Entities

I've been searching all day and can't find a solution to this...
I have an EntityCollection of Communication objects which each have an instance of an Intention object(one-to-one).
I also have a User object which has many instances of UserLocation EntityObjects(one-to-many)
Intention objects have a property UID.
UserLocation objects have a property LID.
I want to write a LINQ expression which returns all Communication objects where the UID property of the Intention instance associated to a Communication object equals ANY LID property of ANY instance of a UserLocation instance for a User object.
I've tried this
return _context.Communications.Where
(u => u.Intention.UID.Equals
(user.UserLocations.Select
(p => p.LID)));
and this
return _context.Communications.Where
(u => user.UserLocations.Any
(x => x.LID.Equals
(u.Intention.UID)));
and this
var thislist = from Intentions in _context.Intentions
join UserLocations in user.UserLocations
on Intentions.UID equals UserLocations.LID
select Intentions.UID;
return _context.Communications.Where(u => u.Intention.Equals(thislist.Any()));
and this
var lidlist = user.UserLocations.Select(x => x.LID);
return _context.Communications.Where(x=> lidlist.Contains(x.Intention.UID)).ToList();
(this gives me an error on the Contains statement saying "Delegate System.Func<Communication,int,bool> does not take 1 argument", don't know how to fix)
Along with all these variations I have also:
modified my method to return IQueryable<Communication> and have also tried List<Communication> while appending ToList() to my queries.
Nothing works. Regardless of what I try I always end up with this exception
NotSupportedException was unhandled by user code
Unable to create a constant value of type 'PreparisCore.BusinessEntities.UserLocation'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
What am i doing wrong??
Given this code:
namespace CollectionsWithIntentions
{
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
internal class Program
{
#region Methods
private static void Main(string[] args)
{
var communications = new[]
{
new Communication { Intention = new Intention { UID = 1 } },
new Communication { Intention = new Intention { UID = 2 } },
new Communication { Intention = new Intention { UID = 3 } },
new Communication { Intention = new Intention { UID = 4 } },
};
var users = new[]
{
new User { UserLocations = new List<UserLocation>(new[] { new UserLocation { LID = 2 },new UserLocation{LID=5} }) },
new User { UserLocations = new List<UserLocation>(new[] { new UserLocation { LID = 3 } }) }
};
IEnumerable<Communication> res =
communications.Where(w => users.Any(a => a.UserLocations.Any(b=>b.LID == w.Intention.UID)));
foreach (Communication communication in res)
{
Trace.WriteLine(communication);
}
}
#endregion
}
internal class Communication
{
#region Public Properties
public Intention Intention { get; set; }
#endregion
#region Public Methods and Operators
public override string ToString()
{
return string.Concat("Communication-> Intention:", this.Intention.UID);
}
#endregion
}
internal class Intention
{
#region Public Properties
public int UID { get; set; }
#endregion
}
internal class User
{
#region Public Properties
public List<UserLocation> UserLocations { get; set; }
#endregion
}
internal class UserLocation
{
#region Public Properties
public int LID { get; set; }
#endregion
}
}
I get this result:
Communication-> Intention:2
Communication-> Intention:3
Am I missing anything?
From the last two compiler errors you have linked in one of your comments...
...I would conclude that Intention.UID is a nullable type int? and not a not-nullable int as you said in the comments. This indeed doesn't compile. Try to change your last query to:
var lidlist = user.UserLocations.Select(x => x.LID);
return _context.Communications
.Where(x => x.Intention.UID.HasValue
&& lidlist.Contains(x.Intention.UID.Value))
.ToList();
The other three queries do not work because user.UserLocations is a collection of a non-primitive custom type in memory (for the SQL query to be generated it is a "constant" value) and EF doesn't support to build a SQL query with such a constant custom type.

Categories

Resources