webForumDet fria alternativet

Referenskonflikt mellan projekt

.NET

9 svar · 509 visningar · startad av doggelito

Medlem sedan juni 20003 076 inlägg
Frågan#1

Hmm, hur gör jag nu!?
Har två projekt i VS, ett entitetsprojekt där entitetsklasser ligger och ett affärsprojekt där bla. metoder för att hämta entiteter ligger.
I affärsprojektet har jag en referens till entitetsprojektet så att jag kan hämta singelentiteter och listor. inget konstigt med det!
Här kommer problemet:
Nu vill jag ifrån entitetsprojektet skapa en referens till affärsprojektet pga. att en av mina entitetsklasser ser ut så här:

public class Category
{
     private inte id;
     ...
     private IList<Category> childCategories = null;

     public int Id
     ...

     public IList<Category> ChildCategories
     {
          get
          {
               if(childCategories == null)
               {
                    CategoryManager cm = new CategoryManager();
                    childCategories = cm.GetCategoriesById(id);
               }
               return childCategories;
           }
           set { childCategories = value };
     }

Men jag får "adding this project as a reference would cause a circular dependency" när jag försöker lägga till projektet.

Vad är best practice här?
Hur kan jag fylla mina childCategories om jag inte får ha en referens till affärsprojektet.

Är enda lösningen att lägga de båda projekten i samma namespace?

Medlem sedan jan. 20022 440 inlägg
#2

Då har du nog inte tänkt till ordentligt när du planerar projekten eftersom du kör med entiteter så är du ju inne på någon typ av ddd-modell. Ditt entitetsprojekt har en referens till affärsprojektet i ditt fall vilket är totalfel.

Dina entiteter ska vara helt omedvetna om alla andra projekt. Ditt entitetsprojekt ska ej ha några referenser till andra projekt.

Sedan lägger du bara till en referens till entitetsprojektet i ditt affärsprojekt.

Om inte detta går av vilken anlednig det än kan vara så får du köra lite refactoring och helt enkelt modellera om. Frågan du bör ställa dig här är väl följande: -"Bör jag ha någon logik i entitetsprojektet?"

Det finns andra modeller som kanske skulle passa dig lite bättre, du kanske ska kolla på: DDD +Repository +Factory.

Det känns lite som om du försöker använda två "modeller" samtidigt.

Medlem sedan juni 20003 076 inlägg
#3

catZ skrev:

Bör jag ha någon logik i entitetsprojektet?

Hmm, nej kanske inte det.
Men jag gillar idén med lazy binding (eller hur det nu stavas) så att childCategories bara bind när jag behöver dem.
Skulle jag lyfta all logik till affären så måste jag ju alltid binda den va, eller?
Det skulle då kunna se ut:

public IList<Category> GetCategories()
{
     IList<Category> cat = ...
     IList<Category> cat.childCategories = ...
     return cat;
}

Men det känns inge roligt!

Medlem sedan juni 20003 076 inlägg
#4

Och det är ju faktiskt värre än så kom jag på nu.
Om GetCategories ska innehålla alla kategorier och dess underkategorier redan från start så måste jag fylla alla childs rekursivt i en loop. Ännu sämre! :(

Medlem sedan jan. 20022 440 inlägg
#5

Vet inte riktigt vad det är du försöker göra i kod. Du behöver inte ladda allting i en loop rekursivt, det finns lösningar runt problemet och den enklaste blir väl att ha ett lager till. :)

Medlem sedan maj 20012 812 inlägg
#6

doggelito skrev:

Är enda lösningen att lägga de båda projekten i samma namespace?

Nope, en vanlig lösning här är att ha ett helt fristående projekt som kan hämta data till dig istället för att behöva skriva den koden själv.. Om du hade använt en O/R Mapper så skulle du utan problem kunna skriva in din LazyLoading i dina entiter och kalla på O/R Mappern därifrån.

En lösning....

En annan är helt enkelt att du flyttar ut logiken för LazyLoadingen ut i ditt BLL / UI. Säg att du vill visa alla underkategorier för en kategori så blir det i din UI så här istället.

Category c = categoryManager.Load(ID=1);

if(c.Children == null)
 c.Children = categoryManager.LoadChildren(ID=1);
.
.
.

Inte lika smidigt som LazyLoading från själva entiteten, men det går att lösa...

En liten men viktig detalj här är att du inte skickar tillbaka NULL om du inte får någon dataifrån din LoadChildren() metod utan en tom lista istället, för om du skickar tillbaka NULL (för det finns inga children till din categori) så kommer du ju även nästa gång gå till databasen och hämta alla barn, och det är ju inte så bra ur prestanda synpunkt, när du vet att det inte finns några...

- M

Medlem sedan jan. 20022 440 inlägg
#7

Null Object :) Det är väldigt användbart...

Medlem sedan juni 20003 076 inlägg
#8

Gladh skrev:

Nope, en vanlig lösning här är att ha ett helt fristående projekt som kan hämta data till dig istället för att behöva skriva den koden själv.. Om du hade använt en O/R Mapper så skulle du utan problem kunna skriva in din LazyLoading i dina entiter och kalla på O/R Mappern därifrån.

Precis som CatZ skriver så skulle jag nog behöva läsa på om olika designs.
Och grejjen är den att jag använder NHibernate som OR! :)

Jag bli alldelens snurrig av det här!

Det här har jag nu:
Ett entitetsprojekt, ett affärsprojekt och NHibernate.

Gladh, menar du att jag ska gå direkt till OR från min property i Categoryklassen?
Typ:

        public virtual IList<Category> ChildCategories
        {
            get {
                if (childCategories == null)
                {
                    Configuration cfg = new Configuration();
                    cfg.AddAssembly(AssamblyName);
                    ISessionFactory factory = cfg.BuildSessionFactory();
                    Session = factory.OpenSession();
                    childCategories = base.Session.CreateCriteria(typeof(Category)).List<Category>();
                }
                    return childCategories; }
            set { childCategories = value; }
        }

Det känns ju fel förståss eftersom jag har en identisk metod i affärslagret som gör samma sak, GetCategoriesById(int id) :OO

Man kanske inte lägger OR metoder i affärslagret alls eller?

Medlem sedan maj 20012 812 inlägg
#9

doggelito skrev:

Det känns ju fel förståss eftersom jag har en identisk metod i affärslagret som gör samma sak, GetCategoriesById(int id)

Japp visst gör det, men "thats life".

doggelito skrev:

Man kanske inte lägger OR metoder i affärslagret alls eller?

Jodå, där eller i ett annat lager mellan BLL och O/R Mappern. Mest för att man enkelt skulle kunna byta ut varifrån datan hämtas utan att behöva ändra överallt i koden, enklare att bara byta ut en assembly och så kör koden vidare.

Så vill du ha lazyloading i din entiteter, för det känns enkelt och praktiskt, så får man tumma lite på desginmönsterna. Man skulle säkert kunna bygga någon generisc metod i ditt BLL som löser det hela åt dig genom att sätta attribut på dina variabler. Nu spånar jag på friskt här, men kanske något sånt här.

i din entitet så markerar du vad som skall lazyloadas.

public class Categroy{

   private long _Identifier;

   [LazyLoading(Name=Children,LazyLoadingID=_Identifier)]
   private List<Category> _CategoryList;
   ...
}

Och i ditt BLL så har den en metod som tar emot en typ och namnet på den lazyloading som man vill göra.


public T LazyLoading<T>(T itemType, string name)
{
    //-- Hitta attributet för lazyloading där name=name i attributet

    //-- Hitta värdet på LazyLoadingID så vi kan göra vårt urval från databasen
    long id = ((FieldInfo)itemType.GetType().GetField(LazyLoadingID).GetValue(itemType);

    //-- Använd dig NHibernat kod för att hämta värden, typ så här
    Configuration cfg = new Configuration();
    cfg.AddAssembly(AssamblyName);
    ISessionFactory factory = cfg.BuildSessionFactory();
    Session = factory.OpenSession();
    List<T> itemTypeList= base.Session.CreateCriteria(typeof(T)).List<T>(id);

    //-- leta upp fältet som har LazyLoadingAttributet och sätt värdet till detta fält
    //-- eller så returnerar du bara List<T> och sätter det själv i din kod där du kallar 
    //-- på metoden ifrån.
    ... 
}

Om du gör något sånt här, så kommer du slippa skriva massor med kod, för varje metod som du vill göra lazyloading på, plus att du slipper ha denna logik i dina entiteter.

- M

Medlem sedan juni 20003 076 inlägg
#10

Gladh, super! :bire Verkar riktigt intressant, måste testas! :)
Sen måste jag erkänna (efter ha läst på lite) att jag missuppfattat kapaciteten med OR mappern.
Om man bara fyller i mappningsfilen korrekt så sker ju det mesta med automatik! ;)
Ex. En kategori har ju en parent kategori och då kan det se ut (om någon är intresserad):

public class Category
{
     ...
     private Datetime createDate;
     ...
     private Category parent;
     public virtual Category Parent
        {
            get { return parent; }
            set { parent = value; }
        }
...
}

<%
     Category cat = (Category)session.Load(typeof(Category), 2); //hämtar kategori med id = 2
     Response.Write(cat.Parent.CreateDate.ToString());
%>

Kategorin hämtar alltså automatiskt sin parent och skriver ut ett värde. Fiffigt! :)

261 ms totalt · 4 externa anrop · v20260731065814-full.a51de22e
119 ms — deklarationer (db)
0 ms — hämta statistik (cache)
135 ms — hämta tråd, inlägg och bilagor (db)
122 ms — ändringar (db)